# Copyright (c) 2010 Flowerfire, Inc. All Rights Reserved. wowza_media_server_pro = { plugin_version = "2.3.1" info.1.manufacturer = "Wowza Media Systems" info.1.manufacturer.url = "http://www.wowzamedia.com/" info.1.device = "Wowza Media Server" info.1.version.1 = "1.1.1" info.1.version.2 = "1.6.0" # build9437 info.1.version.3 = "1.7.0" # build11414 info.1.version.4 = "2.0.0" # build22912 # 2007-05-18 - Wowza - 1.1.1 - Created initial plug-in, based on log from # Macromedia Flash Media Server 2.0.1 r27. # 2008-02-22 - GMF - 1.1.2 - added support for geographic location report # 2008-12-11 - gas - 1.1.3 - force a x_stream_id log field in case there is not one in the log # 2009-02-19 - GMF - 1.1.4 - Added check and workaround for negative x-duration values # 2009-08-31 - GMF - 1.1.5 - Added code to zero stream duration for non-play events # 2009-09-16 - GMF - 1.1.6 - FIXED code to zero stream duration for non-play events # 2009-09-29 - GMF - 1.2 - Added support for publish_duration # 2009-09-29 - GMF - 1.2 - Added support for publish_duration # 2009-11-11 - GMF - 1.2.1 - Removed x_duration as a non-aggregating field # 2009-12-08 - KBB - 1.3 - Changed the way play duration is distinguished from non-play duration. # Restored full tracking of x_duration in stream_duration and added the play_duration and # pause_duration numeric fields. Added check for no publishing start time because we have one # log sample where there are no c_client_id values on unpublish events. Backed out 1.1.3 - corrected # the problem by not checking for x_stream_id in fix_session_durations. # 2009-12-09 - KBB - 1.3.1 - Eliminated compute_pause_duration and set pause_duration directly. # 2009-12-31 - KBB - 1.4 - Added basic sessions. Eliminated session_duration and sessions numeric # fields, to avoid confusion. (Changed names to wsessions_duration and wsessions during testing.) # 2010-03-30 - GMF - 1.4.1 - Improved autodetect to detect non-Pro version of Software line # 2011-11-18 - GMF - 1.5 - Changed all the last_* tracking from nodes to collected entries, to allow for fast garbage collection. # 2011-12-14 - GMF - 1.5.1 - Fixed an issue with calculation of durations, where differencing was not occuring properly # 2012-01-11 - GMF - 1.5.2 - Added average_play_duration_per_stream field; renamed duration_per_stream to average_stream_duration_per_stream. # 2012-02-24 - GMF - 1.6 - Removed sessions; added concurrent connections; omitted x_comment. # 2012-03-08 - GMF - 1.6.1 - Added x_comment requirement to x_comment filter # 2012-04-04 - GMF - 1.6.2 - Changed log_format_type to "media_server" # 2012-04-07 - GMF - 2.0 - Implemented media_reports snapon. Implemented referrer_analysis snapon. Categorized reports. Removed some fields (file size and x-spos). # 2012-04-07 - GMF - 2.1 - Implemented sc_stream_bytes and cs_stream_bytes using snapons # 2012-04-16 - GMF - 2.1.1 - Added requires_database_fields c_referrer to referrer snapon # 2012-05-11 - GMF - 2.1.2 - Fixed stream_duration to connection_duration in concurrent snapon # 2012-05-16 - GMF - 2.1.3 - Fixed stream_duration to connection_duration in all other places # 2012-06-12 - GMF - 2.1.4 - Switched to concurrent_sessions_wowza snapon # 2012-06-13 - GMF - 2.1.5 - Fixed bug where concurrent_sessions_wowza snapon node name did not match "name" subnode # 2012-06-14 - GMF - 2.1.6 - Added requirement for x_category and x_event in concurrency snapon # 2012-06-14 - GMF - 2.1.7 - Added requirement for database fields in media reports snapon # 2012-06-18 - GMF - 2.1.8 - fixed database field requirement to x_event (error was x_events) # 2012-06-29 - GMF - 2.1.9 - Added support for out-of-order events in a session when calculating bytes (e.g., a "stop" after a "disconnect"); ThreadID:1271691 # 2012-07-10 - GMF - 2.1.10 - Added unique_client_ids field # 2012-07-18 - GMF - 2.2 - Implemented header parsing with filter_preprocessor (to handle issue where agent contained a space, ThreadID:1273312). # 2012-07-24 - GMF - 2.2.1 - Extended header parsing to support table in #Fields # 2012-08-01 - GMF - 2.2.2 - Fixed c_ip to be type host # 2012-08-01 - MSG - 2.2.3 - Put separate reports into report groups # 2012-08-08 - GMF - 2.2.4 - Implemented hierarchy in x_sname (it was hierarchical before, but that was lost when custom header parsing was added). # 2012-08-21 - GMF - 2.2.5 - Changed c_user_agent to a normal string type field, instead of parsing it as though it were an HTTP User-Agent header # 2012-10-19 - GMF - 2.3 - Removed log-filter-based byte analysis, replacing it with snapons # 2012-12-07 - GMF - 2.3.1 - Fixed the position of the referrer group; fixed bug where final field in W3C header was ignored # Here is one session. Tabs are converted to \t. A session may include multiple streams. ##Fields: x-severity\tx-category\tx-event\tdate\ttime\tc-client-id\tc-ip\tc-port\tcs-bytes\tsc-bytes\tx-duration\tx-sname\tx-stream-id\tsc-stream-bytes\tcs-stream-bytes\tx-file-size\tx-file-length\tx-ctx\tx-comment #INFO\tsession\tconnect-pending\t2009-09-29\t01:27:51\t825981741\t11.11.11.11\t-\t3543\t3085\t0.375\t-\t-\t-\t-\t-\t-\t11.11.11.11\t- #INFO\tsession\tconnect\t2009-09-29\t01:27:51\t825981741\t11.11.11.11\t-\t3543\t3085\t0.391\t-\t-\t-\t-\t-\t-\t11.11.11.11\t- #INFO\tstream\tcreate\t2009-09-29\t01:27:51\t825981741\t11.11.11.11\t-\t3593\t3409\t0.0\t-\t58654\t0\t0\t0\t0.0\t-\t- #INFO\tstream\tplay\t2009-09-29\t01:27:51\t825981741\t11.11.11.11\t-\t3650\t3452\t0.093\t900New\t58654\t0\t0\t21171719\t145.133\t900New\t- #INFO\tstream\tpause\t2009-09-29\t01:28:52\t825981741\t11.11.11.11\t-\t3782\t9028015\t60.437\t900New\t58654\t9023743\t0\t21171719\t145.133\t900New\t- #INFO\tstream\tunpause\t2009-09-29\t01:28:53\t825981741\t11.11.11.11\t-\t3816\t9028196\t61.859\t900New\t58654\t9023743\t0\t21171719\t145.133\t900New\t- #INFO\tstream\tstop\t2009-09-29\t01:30:13\t825981741\t11.11.11.11\t-\t3861\t21135965\t141.5\t900New\t58654\t21130600\t0\t21171719\t145.133\t900New\t- #INFO\tstream\tdestroy\t2009-09-29\t01:30:17\t825981741\t11.11.11.11\t-\t3899\t21348746\t145.562\t900New\t58654\t21343001\t0\t-\t-\t900New\t- #INFO\tsession\tdisconnect\t2009-09-29\t01:30:17\t825981741\t11.11.11.11\t-\t3899\t21348746\t146.047\t-\t-\t-\t-\t-\t-\t825981741\t- # The name of the log format log.format.format_label = "Wowza Media Server Log Format" log.miscellaneous.log_data_type = "generic_w3c" log.miscellaneous.log_format_type = "media_server" # The log is in this format if any of the first ten lines match this regular expression # 2010-03-30 - GMF - Changed "Server Pro " to just "Server " because of this example: #Software: Wowza Media Server 2.0.0 build22912 log.format.autodetect_regular_expression = "^#Software: Wowza Media Server " # Don't track these fields in the database by default # auto_setup.omit_database_fields = "x_duration,x_file_size,x_file_length,x_spos,cs_stream_bytes,sc_stream_bytes" # Every 10,000 lines, discard old values from the last_* lists (which are managed as collected entries). log.format.collected_entry_lifespan = 10000 log.format.discard_expired_entries = true # Use tab instead of any whitespace, because fields (e.g., user-agent) can have embedded spaces. ThreadID:1273312. log.format.field_separator = " " # Don't time out session, discard them for being long, or remove reloads statistics.miscellaneous = { #maximum_session_duration = "0" #session_timeout = "0" remove_reloads_from_sessions = "false" } # statistics.miscellaneous log.filter_initialization = ` #v.last_session_duration = 0.0; #v.stream_pause_time = ""; #string converted_c_ip; #string converted_stream_id; float cs_bytes_for_db; float sc_bytes_for_db; float sc_stream_bytes_for_db; float cs_stream_bytes_for_db; float connection_duration_for_db; #float session_duration_for_db; float adj_duration; ` # This handles #Fields lines, and creates log and database fields from them log.filter_preprocessor = ` if (matches_regular_expression(current_log_line(), '^#Fields: (.*)$')) then ( string fields = $1; string fieldname; v.logfieldindex = 1; string numerical_fields = "profiles." . internal.profile_name . ".database.numerical_fields"; # This subroutine creates a database field subroutine(create_database_field(string fieldname), ( #echo("create_database_field: " . fieldname); debug_message("create_database_field(" . fieldname . ")\n"); string databasefieldpath = "profiles." . internal.profile_name . ".database.fields." . fieldname; (databasefieldpath . "") = ""; node databasefield = databasefieldpath; # set_subnode_value(databasefield, "label", fieldname); if (fieldname eq 'x_sname') then databasefield{"suppress_bottom"} = 9; databasefield; )); subroutine(create_log_field(string fieldname, string type, bool withindex), ( debug_message("create_log_field(" . fieldname . "; type=" . type . ")\n"); string logfieldpath = "profiles." . internal.profile_name . ".log.fields." . fieldname; (logfieldpath . "") = ""; node logfield = logfieldpath; # set_subnode_value(logfield, "label", fieldname); if (fieldname eq 'x_sname') then @logfield{"hierarchy_dividers"} = "/"; if (withindex) then ( set_subnode_value(logfield, "index", v.logfieldindex); v.logfieldindex++; ); set_subnode_value(logfield, "subindex", 0); if (type ne '') then set_subnode_value(logfield, "type", type); logfield; )); # Assume there isn't a localtime field until we see one. v.parse_localtime = false; #echo("field: " . fields); # Extract the fields on at a time # 2012-07-24 - Added support for tabs in Fields line, e.g, /pub/logs/Examples/wowza_media_server_pro/wowzamediaserver_stats.log.2012-03-08.2012-03-08.txt while (matches_regular_expression(fields, '^([^ ]+)[ ](.*)$') or matches_regular_expression(fields, '^([^ ]+)$')) ( string unconverted_fieldname = $1; fields = $2; # Clean up the field name fieldname = ''; for (int i = 0; i < length(unconverted_fieldname); i++) ( string c = lowercase(substr(unconverted_fieldname, i, 1)); if (!matches_regular_expression(c, '^[a-z0-9]$')) then c = '_'; fieldname .= c; ); while (matches_regular_expression(fieldname, '^(.*)_$')) fieldname = $1; #echo("fieldname: " . fieldname); # Get the log field type string log_field_type = ''; if (fieldname eq 'cs_uri_stem') then ( log_field_type = 'page'; ); # 2012-08-21 - GMF - c_user_agent isn't a normal user-agent, in Wowza; it's its own weird thing ("WIN 10,1,82,76"). Don't call it type "agent" # else if (fieldname eq 'c_user_agent') then # log_field_type = 'agent'; else if (fieldname eq 'c_referer') then log_field_type = 'url'; else if (fieldname eq 'c_ip') then log_field_type = 'host'; else if (fieldname eq 'x_sname') then log_field_type = 'hierarchical'; # Create the log field #echo("Creating log field: " . fieldname); create_log_field(fieldname, log_field_type, true); if (fieldname eq "c_ip") then ( create_log_field('location', "", false); #echo("CREATED LOCATION"); ); # If we're creating a profile, create the database fields too. if (node_exists("volatile.creating_profile")) then ( #echo("creating database field; fieldname=" . fieldname); # Don't track these fields in the database by default if ((fieldname eq "x_duration") or (fieldname eq "x_file_size") or (fieldname eq "x_file_length") or (fieldname eq "x_spos") or (fieldname eq "cs_stream_bytes") or (fieldname eq "sc_stream_bytes")) then ( ); # Handle date by creating date_time and derived database fields else if (fieldname eq "date") then ( create_database_field('date_time'); create_database_field('day_of_week'); create_database_field('hour_of_day'); ); # if date else if (fieldname eq "time") then ( # create_database_field('date_time'); # create_database_field('day_of_week'); # create_database_field('hour_of_day'); ); # if time # Create derived field for c_ip else if (fieldname eq "c_ip") then ( create_database_field('c_ip'); create_database_field('location'); ); # Create derived fields for referrer else if (fieldname eq "c_referer") then ( create_database_field('search_engine'); create_database_field('search_phrase'); ); # Create derived file type field else if (fieldname eq "cs_uri_stem") then ( create_database_field('file_type'); ); # Don't add a database field for numerical fields else if (numerical_fields?{fieldname}) then ( debug_message("Not adding numerical field: " . fieldname . "\n"); ); # Create a normal database field else create_database_field(fieldname); ); # if creating profile ); # while another field # Don't parse the #Fields line as a data line 'reject'; ); # if #Fields # Don't parse any other # lines as data lines else if (starts_with(current_log_line(), '#')) then ( 'reject'; ); ` log.fields = { # date = "" # time = "" # ## # 11/12/2008 16:17:59 : gas : force a x_stream_id field in case there is not one ## x_stream_id = "" # # # 2008-02-22 - GMF - s_ip was type "host" but that won't work, because # # c_ip is also type "host", and "there can be only one". # s_ip = "" # # c_ip.type = "host" # cs_uri_stem.type = "page" publish_duration = "" connection_duration = "" # wsession_duration = "" play_duration = "" pause_duration = "" # These fields are used with set_collected_field() and get_collected_field() to save information about state. last_cs_bytes = "" last_sc_bytes = "" last_sc_stream_bytes = "" last_cs_stream_bytes = "" last_connection_duration = "" last_stream_state = "" stream_publish_time = "" } # log.fields log.parsing_filters = { # # Get search engine and search phrase information from the referrer field (before it gets simplified). # compute_se_sp = ` #if (get_search_engine_info(c_referrer)) then ( # search_engine = volatile.search_engine; # search_phrase = volatile.search_phrase; #); # ` fix_x_duration = { value = ` # 2009-02-19 - GMF - Some Wowza logs contain negative durations, like below. # If this happens, set them to 0. # #Software: Wowza Media Server Pro 1.5.3 build8082 # INFO stream destroy 2008-11-28 10:50:52 1592290464 12.34.56.78 - 3780 3856 -29567.097 Dir1/File 33387 0 Dir1/File - if (x_duration < 0) then x_duration = 0; ` requires_fields = { x_duration = true } } # fix_x_duration set_adj_stream_id = { value = ` # 2009/12/02 - KBB - We have examples were the c_client_id field exists but contains -. #if (x_stream_id ne "(empty)" and x_stream_id ne "-") then ( if ((x_stream_id ne "(empty)" and x_stream_id ne "-") and (c_client_id ne "(empty)" and c_client_id ne "-")) then ( x_stream_id = c_client_id . '_' . x_stream_id; ); ` requires_fields = { c_client_id = true x_stream_id = true } } # set_adj_stream_id # 2012-10-19 - GMF - Byte calculations are now done with snapons, to provide more accurate counting for multiprocessor builds, and out-of-chronological-order log data. # ## # ## this variant of fix_cs_bytes is used when there is a c_client_id field # ## # # fix_cs_bytes_c_client_id = { # value = ` ## Fix the cs_bytes field by subtracting this value from the previous one (it's a running total, ## which otherwise will be aggregated to give too-large numbers). # #if (c_client_id ne "(empty)" and c_client_id ne "-") then ( # # # If there was a previous value, use the difference in the database entry # if (get_collected_field(c_client_id, 'last_cs_bytes') ne "") then ( # cs_bytes_for_db = cs_bytes - get_collected_field(c_client_id, 'last_cs_bytes'); # if (cs_bytes_for_db < 0) then ( # cs_bytes_for_db = cs_bytes; # ); # ); # # # If there was no previous value, use cs_bytes # else ( # cs_bytes_for_db = cs_bytes; # ); # # # Remember the current cs_bytes value for a later line # if (x_event eq "disconnect") then # set_collected_field(c_client_id, 'last_cs_bytes', 0); # else # set_collected_field(c_client_id, 'last_cs_bytes', cs_bytes); # # # In the database, the cs_bytes field should be the difference # cs_bytes = cs_bytes_for_db; # #); #else ( # cs_bytes = 0; #);# if c_client_id # #` # requires_fields = { # c_client_id = true # cs_bytes = true # x_event = true # } # } # fix_cs_bytes_c_client_id # 2012-10-19 - GMF - Byte calculations are now done with snapons, to provide more accurate counting for multiprocessor builds, and out-of-chronological-order log data. # ## # ## this variant of fix_sc_bytes is used when there is a c_client_id field # ## # # fix_sc_bytes_c_client_id = { # value = ` ## Fix the sc_bytes field by subtracting this value from the previous one (it's a running total, ## which otherwise will be aggregated to give too-large numbers). # ##echo("c_client_id=" . c_client_id . '; sc_bytes=' . sc_bytes . '; x_event=' . x_event); #if (c_client_id ne "(empty)" and c_client_id ne "-") then ( # # # If this session has already disconnected, ignore any further bytes (handles case where "stop" events can occur out of sequence, ThreadID:1271691). # if (get_collected_field(c_client_id, 'last_sc_bytes') eq 'disconnected') then ( # sc_bytes_for_db = 0; # ); # # # If there was a previous value, use the difference in the database entry # else if (get_collected_field(c_client_id, 'last_sc_bytes') ne '') then ( ##echo("last_sc_bytes=" . get_collected_field(c_client_id, 'last_sc_bytes')); # sc_bytes_for_db = sc_bytes - get_collected_field(c_client_id, 'last_sc_bytes'); # if (sc_bytes_for_db < 0) then ( # sc_bytes_for_db = sc_bytes; # ); # ); # if last_sc_bytes ne '' # # # If there was no previous value, use sc_bytes # else ( # sc_bytes_for_db = sc_bytes; # ); # ## echo("sc_bytes_for_db=" . sc_bytes_for_db); # # # Remember the current sc_bytes value for a later line ## if (x_event eq "disconnect") then # set_collected_field(c_client_id, 'last_sc_bytes', 'disconnected'); # else # set_collected_field(c_client_id, 'last_sc_bytes', sc_bytes); # # # In the database, the sc_bytes field should be the difference # sc_bytes = sc_bytes_for_db; # #); # if c_client_id # #else ( # sc_bytes = 0; #); # if c_client_id # #` # requires_fields = { # c_client_id = true # sc_bytes = true # x_event = true # } # } # fix_sc_bytes_c_client_id # 2012-10-19 - GMF - Byte calculations are now done with snapons, to provide more accurate counting for multiprocessor builds, and out-of-chronological-order log data. # ## # ## this variant of fix_sc_stream_bytes is used when there is a c_client_id field # ## # # fix_sc_stream_bytes_c_client_id = { #value = ` ## Fix the sc_bytes field by subtracting this value from the previous one (it's a running total, ## which otherwise will be aggregated to give too-large numbers). # #if (x_stream_id ne "(empty)" and x_stream_id ne "-") then ( # # # 2009/12/08 - KBB - Stream ID is already adjusted. ## converted_stream_id = c_client_id . '_' . x_stream_id; # # # If this session has already disconnected, ignore any further bytes (handles case where "stop" events can occur out of sequence, ThreadID:1271691). # if (get_collected_field(c_client_id, 'last_sc_bytes') eq 'disconnected') then ( # sc_bytes_for_db = 0; # ); # # # If there was a previous value, use the difference in the database entry # else if (get_collected_field(x_stream_id, 'last_sc_stream_bytes') ne '') then ( # sc_stream_bytes_for_db = sc_stream_bytes - get_collected_field(x_stream_id, 'last_sc_stream_bytes'); # if (sc_stream_bytes_for_db < 0) then ( # sc_stream_bytes_for_db = sc_stream_bytes; # ); # ); # # # If there was no previous value, use sc_bytes # else ( # sc_stream_bytes_for_db = sc_stream_bytes; # ); # # # Remember the current sc_bytes value for a later line # if (x_event eq "destroy") then ( # set_collected_field(x_stream_id, 'last_sc_stream_bytes', 'disconnected'); # ); # else ( # set_collected_field(x_stream_id, 'last_sc_stream_bytes', sc_stream_bytes); # ); # # # In the database, the sc_bytes field should be the difference # sc_stream_bytes = sc_stream_bytes_for_db; # #); #else ( # sc_stream_bytes = 0; #); # if x_stream_id # #` # # requires_fields = { # c_client_id = true # x_stream_id = true # sc_stream_bytes = true # x_event = true # } # } # fix_sc_stream_bytes_c_client_id # # ## # ## this variant of fix_cs_stream_bytes is used when there is a c_client_id field # ## # # fix_cs_stream_bytes_c_client_id = { # value = ` ## Fix the sc_bytes field by subtracting this value from the previous one (it's a running total, ## which otherwise will be aggregated to give too-large numbers). # #if (x_stream_id ne "(empty)" and x_stream_id ne "-") then ( # # # 2009/12/08 - KBB - Stream ID is already adjusted. ## converted_stream_id = c_client_id . '_' . x_stream_id; # # # If this session has already disconnected, ignore any further bytes (handles case where "stop" events can occur out of sequence, ThreadID:1271691). # if (get_collected_field(c_client_id, 'last_cs_bytes') eq 'disconnected') then ( # cs_bytes_for_db = 0; # ); # # # If there was a previous value, use the difference in the database entry # else if (get_collected_field(x_stream_id, 'last_cs_stream_bytes') ne '') then ( # cs_stream_bytes_for_db = cs_stream_bytes - get_collected_field(x_stream_id, 'last_cs_stream_bytes'); # if (cs_stream_bytes_for_db < 0) then ( # cs_stream_bytes_for_db = cs_stream_bytes; # ); # ); # # # If there was no previous value, use sc_bytes # else ( # cs_stream_bytes_for_db = cs_stream_bytes; # ); # # # Remember the current sc_bytes value for a later line # if (x_event eq "destroy") then ( # set_collected_field(x_stream_id, 'last_cs_stream_bytes', 'disconnected'); # ); # else ( # set_collected_field(x_stream_id, 'last_cs_stream_bytes', cs_stream_bytes); # ); # # # In the database, the sc_bytes field should be the difference # cs_stream_bytes = cs_stream_bytes_for_db; # #); #else ( #cs_stream_bytes = 0; #); # if x_stream_id # #` # requires_fields = { # c_client_id = true # x_stream_id = true # cs_stream_bytes = true # x_event = true # } # } # fix_cs_stream_bytes_c_client_id ## ## fix_connection_durations ## fix_connection_durations = { value = ` if (x_category eq "stream" and (x_stream_id ne "(empty)" and x_stream_id ne "-")) then ( # 2009/12/08 - KBB - Stream ID is already adjusted. # converted_stream_id = c_client_id . '_' . x_stream_id; adj_duration = x_duration*1000.0; # If there was a previous value, use the difference in the database entry if (get_collected_field(x_stream_id, 'last_connection_duration') ne '') then ( connection_duration_for_db = adj_duration - get_collected_field(x_stream_id, 'last_connection_duration'); if (connection_duration_for_db < 0.0) then ( connection_duration_for_db = adj_duration; ); ); # If there was no previous value, use adj_duration else ( connection_duration_for_db = adj_duration; ); #echo("### connection_duration_for_db=" . connection_duration_for_db); # Remember the current sc_bytes value for a later line # 2009-08-31 - GMF - remember the x-event too, so we can use 0 for non-play events. # 2009-12-08 - KBB - remember the state instead of the x-event, playing or paused if (x_event eq "destroy") then ( set_collected_field(x_stream_id, 'last_connection_duration', 0.0); ); else ( set_collected_field(x_stream_id, 'last_connection_duration', adj_duration); ); # # 2009-08-31 - GMF - If the last event wasn't "play", the play duration of it is 0. # if (node_value(subnode_by_name('v.last_stream_x_event', x_stream_id)) ne "play") then ( # connection_duration_for_db = 0; # ); # In the database, the sc_bytes field should be the difference connection_duration = connection_duration_for_db/1000.0; # 2009-12-08 - KBB - set durations based on state if (get_collected_field(x_stream_id, 'last_stream_state') eq "playing") then ( play_duration = connection_duration; ); else if (get_collected_field(x_stream_id, 'last_stream_state') eq "paused") then ( pause_duration = connection_duration; ); # 2009-12-08 - KBB - remember the state instead of the x-event, playing or paused if ((x_event eq "destroy") or (x_event eq "stop")) then ( set_collected_field(x_stream_id, 'last_stream_state', ''); ); else if ((x_event eq "play") or (x_event eq "unpause")) then ( set_collected_field(x_stream_id, 'last_stream_state', 'playing'); ); else if (x_event eq "pause") then ( set_collected_field(x_stream_id, 'last_stream_state', 'paused'); ); # else, leave it alone ); # if fix_connection_durations ` requires_fields = { c_client_id = true x_stream_id = true x_duration = true x_category = true x_event = true } } # fix_connection_durations compute_publish_duration = { value = ` # If this is a publish event, remember when it was if (x_event eq "publish") then ( v.current_date_time_epoc = date_time_to_epoc(normalize_date(date, 'auto') . ' ' . normalize_time(time, 'auto')); #echo("v.current_date_time_epoc: " . v.current_date_time_epoc); set_collected_field(x_stream_id, 'stream_publish_time', v.current_date_time_epoc); ); # If this is an unpublish event, remember the difference between it # and its publish event, in the publish_duration field. else if (x_event eq "unpublish") then ( v.current_date_time_epoc = date_time_to_epoc(normalize_date(date, 'auto') . ' ' . normalize_time(time, 'auto')); #echo("v.current_date_time_epoc: " . v.current_date_time_epoc); v.publish_start_time = get_collected_field(x_stream_id, 'stream_publish_time'); # 2009/12/08 - KBB - Make sure there is a start time to avoid huge inaccurate durations. if (v.publish_start_time > 0) then publish_duration = v.current_date_time_epoc - v.publish_start_time; else publish_duration = 0; ); ` requires_fields = { x_event = true x_stream_id = true } # requires_fields } # compute_publish_duration # compute_pause_duration = { # # value = ` # ## If this is a pause event, remember when it was #if (x_event eq "pause") then ( # v.current_date_time_epoc = date_time_to_epoc(normalize_date(date, 'auto') . ' ' . normalize_time(time, 'auto')); # #echo("v.current_date_time_epoc: " . v.current_date_time_epoc); # set_subnode_value('v.stream_pause_time', x_stream_id, v.current_date_time_epoc); #); # ## If this is an unpause event, remember the difference between it ## and its pause event, in the pause_duration field. #else if (x_event eq "unpause") then ( # v.current_date_time_epoc = date_time_to_epoc(normalize_date(date, 'auto') . ' ' . normalize_time(time, 'auto')); # #echo("v.current_date_time_epoc: " . v.current_date_time_epoc); # v.pause_start_time = node_value(subnode_by_name('v.stream_pause_time', x_stream_id)); # if (v.pause_start_time > 0) then # pause_duration = v.current_date_time_epoc - v.pause_start_time; # else # pause_duration = 0; #); # #` # requires_fields = { # x_event = true # x_stream_id = true # connection_duration = true # } # requires_fields # # } # compute_pause_duration # ## # ## fix_session_durations # ## # # fix_session_durations = { # value = ` ## Fix the session_duration field by subtracting this value from the previous one (it's a running total, ## which otherwise will be aggregated to give too-large numbers). # ## 2009/12/08 - KBB - We don't need to know about x_stream_id hear. ##if (x_category eq "session" and ((c_client_id ne "(empty)" and c_client_id ne "-") and (x_stream_id eq "(empty)" or x_stream_id eq "-"))) then ( #if (x_category eq "session" and (c_client_id ne "(empty)" and c_client_id ne "-")) then ( # # adj_duration = x_duration*1000.0; # # # If there was a previous value, use the difference in the database entry # if (subnode_exists('v.last_session_duration', c_client_id)) then ( # session_duration_for_db = adj_duration - node_value(subnode_by_name('v.last_session_duration', c_client_id)); # if (session_duration_for_db < 0.0) then ( # session_duration_for_db = adj_duration; # ); # ); # # # If there was no previous value, use sc_bytes # else ( # session_duration_for_db = adj_duration; # ); # # # Remember the current sc_bytes value for a later line # if (x_event eq "disconnect") then ( # set_subnode_value('v.last_session_duration', c_client_id, 0.0); # ); # else ( # set_subnode_value('v.last_session_duration', c_client_id, adj_duration); # ); # # # In the database, the sc_bytes field should be the difference # wsession_duration = session_duration_for_db/1000.0; # #); # if fix_session_durations #` # requires_fields = { # c_client_id = true # x_duration = true # x_category = true # x_event = true ## x_stream_id = true # } # } # fix_session_durations } # Log.parsing_filters log.filters = { # Now added by snapon # simplify_referrer = { # label = "$lang_admin.log_filters.simplify_referrer_label" # comment = "$lang_admin.log_filters.simplify_c_referrer_comment" # value = "if (c_referrer eq '-') then c_referrer = '(no referrer)' else if (matches_regular_expression(c_referrer, '^([^:]+://[^/]+/)')) then c_referrer = $1 . '(omitted)'" # } # simplify_referrer omit_x_comment = { label = "Omit x_comment (for a smaller database)" value = "x_comment = '[omitted by log filter]'" requires_fields = { x_comment = true } } # omit_x_comment mark_entry = { label = '$lang_admin.log_filters.mark_entry_label' comment = '$lang_admin.log_filters.mark_entry_comment' value = 'events = 1;' } # mark_entry } # log.filters # database.fields.location = "" database.numerical_fields = { events = { default = false requires_log_field = false } unique_client_ids = { requires_log_field = true log_field = "c_client_id" type = "unique" } # unique_client_ids # streams = { # default = true # requires_log_field = true # log_field = "x_stream_id" # type = "unique" # } # streams unique_client_ips = { default = true requires_log_field = true log_field = "c_ip" type = "unique" } # unique_client_ips # sc_bytes = { # default = true # type = "int" # integer_bits = 64 # display_format_type = "bandwidth" # } # # cs_bytes = { # default = false # type = "int" # integer_bits = 64 # display_format_type = "bandwidth" # } # sc_stream_bytes = { # default = false # type = "int" # integer_bits = 64 # display_format_type = "bandwidth" # } # # cs_stream_bytes = { # default = false # type = "int" # integer_bits = 64 # display_format_type = "bandwidth" # } # 2012-04-17 - GMF - Changed stream_duration to connection_duration in the hope that its meaning will be more intuitive. connection_duration = { default = true type = "int" integer_bits = 64 display_format_type = "duration_compact" } # wsession_duration = { # default = true # type = "int" # integer_bits = 64 # display_format_type = "duration_compact" # } play_duration = { default = true type = "int" integer_bits = 64 display_format_type = "duration_compact" } # 2012-04-17 - GMF - Commented out pause_duration, because who actually cares about this field? It's just UI clutter. # pause_duration = { # default = false # type = "int" # integer_bits = 64 # display_format_type = "duration_compact" # } # 2012-04-17 - GMF - Commented out pause_duration, because who actually cares about this field? It's just UI clutter. # publish_duration = { # default = false # type = "int" # integer_bits = 64 # display_format_type = "duration_compact" # } # 2012-04-17 - GMF - Commented out average_stream_duration_per_stream; this is a somewhat confusing field, and can be added by snapon if required. Will consider re-enabling it, if anyone notices it's gone.... # average_stream_duration_per_stream = { # default = true # log_field = "stream_duration" # requires_log_field = true # type = "int" # integer_bits = 64 # aggregation_method = "average" # average_denominator_field = "streams" # display_format_type = duration_compact # } # 2012-04-17 - GMF - Commented out average_play_duration_per_stream; this is a somewhat confusing field, and can be added by snapon if required. Will consider re-enabling it, if anyone notices it's gone.... # average_play_duration_per_stream = { # default = true # log_field = "play_duration" # requires_log_field = true # type = "int" # integer_bits = 64 # aggregation_method = "average" # average_denominator_field = "streams" # display_format_type = duration_compact # } } # database.numerical_fields # log.field_options = { # # sessions_page_field = "x_sname" # sessions_visitor_id_field = "c_ip" # sessions_event_field = "events" # sessions_id_field = "c_client_id" # # } # log.field_options create_profile_wizard_options = { # How the reports should be grouped in the report menu report_groups = { date_time_group = { date_time = "" year = "" month = "" day = "" day_of_week = "" hour_of_day = "" tz = "" } content_group = { x_sname = "" x_stream_id = "" x_ctx = "" s_uri = "" x_sname_query = "" file_type = "" x_file_name = "" x_file_ext = "" x_suri = "" x_suri_stem = "" x_suri_query = "" } # content_group client_group = { c_ip = "" c_client_id = "" location = "" domain_description = "" isp = "" domain = "" organization = "" country = "" region = "" city = "" c_port = "" c_user_agent = "" c_proto = "" } # client_group visitor_systems_group = { operating_system = "" web_browser = "" } server_group = { x_vhost = "" x_app = "" x_appinst = "" x_status = "" s_ip = "" s_port = "" } # server_group referrer_group = { c_referrer = "" search_phrase = "" search_engine = "" } # referrer_group other_group = { x_severity = "" x_category = "" x_event = "" x_ctx = "" x_comment = "" } # other_group } # report_groups snapons = { # Add the sc_stream_bytes field as an aggregating field sc_stream_bytes = { snapon = "aggregating_field" name = "sc_stream_bytes" label = "sc-stream-bytes" prompt_to_attach = true prompt_to_attach_default = false parameters = { field_name.parameter_value = "$lang_stats.field_labels.sc_stream_bytes" field_name.final_node_name = "sc_stream_bytes" log_field.parameter_value = "sc_stream_bytes" display_format_type.parameter_value = "bandwidth" } # parameters } # sc_stream_bytes # Add the cs_stream_bytes field as an aggregating field cs_stream_bytes = { snapon = "aggregating_field" name = "cs_stream_bytes" label = "cs-stream-bytes" prompt_to_attach = true prompt_to_attach_default = false parameters = { field_name.parameter_value = "$lang_stats.field_labels.cs_stream_bytes" field_name.final_node_name = "cs_stream_bytes" log_field.parameter_value = "cs_stream_bytes" display_format_type.parameter_value = "bandwidth" } # parameters } # cs_stream_bytes # Attach a media_reports snapon media_reports = { snapon = "media_reports" name = "media_reports" label = "$lang_admin.snapons.media_reports.label" parameters = { user_field.parameter_value = "c_ip" # have_category_field.parameter_value = false # category_field.parameter_value = "category" # domain_field.parameter_value = "top_level_domain" duration_field.parameter_value = "play_duration" stream_name_field.parameter_value = "x_sname" stream_id_field.parameter_value = "x_stream_id" } # parameters requires_database_fields = { c_ip = true play_duration = true x_sname = true x_stream_id = true } # requires_database_fields } # media_reports # Add the standard reports add_standard_reports = { name = "add_standard_reports" label = "add_standard_reports" snapon = "add_standard_reports" } # add_standard_reports # Attach the referrer_analysis snapon referrer_analysis = { snapon = "referrer_analysis" name = "referrer_analysis" label = "$lang_admin.snapons.referrer_analysis.label" prompt_to_attach = true prompt_to_attach_default = false parameters = { referrer_field.parameter_value = "c_referrer" } # parameters requires_database_fields = { c_referrer = true } # requires_database_fields } # referrer_analysis # Attach a concurrent_events snapon to compute concurrent connections # concurrent_connections = { # snapon = "concurrent_events" # name = "concurrent_connections" # label = "$lang_admin.snapons.plugins.flash_media_server.concurrent_connections" # parameters = { # date_time_field.parameter_value = "date_time" # duration_field.parameter_value = "connection_duration" # concurrent_events_name = { # parameter_value = "$lang_admin.snapons.plugins.flash_media_server.concurrent_connections" # final_node_name = "concurrent_connections" # } # } # parameters # # requires_database_fields = { # date_time = true ## connection_duration = true # } # # } # concurrent_connections # Attach a concurrent_streams_wowza snapon to compute concurrent connections concurrent_streams_wowza = { snapon = "concurrent_streams_wowza" name = "concurrent_streams_wowza" label = "$lang_admin.snapons.plugins.wowza_media_server.concurrent_streams" parameters = { date_time_field.parameter_value = "date_time" x_category_field.parameter_value = "x_category" x_event_field.parameter_value = "x_event" x_category_start_value.parameter_value = "stream" x_category_end_value.parameter_value = "stream" x_event_start_value.parameter_value = "play" x_event_end_value.parameter_value = "stop" concurrent_streams_name = { parameter_value = "$lang_admin.snapons.plugins.wowza_media_server.concurrent_streams" final_node_name = "concurrent_streams" } # track_per_resource.parameter_value = true # resource_field.parameter_value = "x_sname" } # parameters requires_database_fields = { date_time = true x_category = true x_event = true } } # concurrent_streams_wowza } # snapons } # create_profile_wizard_options } # wowza_media_server_pro