# Copyright (c) 2010 Flowerfire, Inc. All Rights Reserved. wowza_media_server_pro = { plugin_version = "1.4.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 # 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 = "web_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 x_duration as an aggregating field, or as a numerical field--we'll use its value to compute other fields. auto_setup.omit_database_fields = "x_duration" # 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_cs_bytes = ""; v.last_sc_bytes = ""; v.last_sc_stream_bytes = ""; v.last_cs_stream_bytes = ""; v.last_stream_duration = 0.0; #v.last_stream_x_event = ""; v.last_stream_state = ""; #v.last_session_duration = 0.0; v.stream_publish_time = ""; #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 stream_duration_for_db; #float session_duration_for_db; float adj_duration; ` 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 = "" stream_duration = "" # wsession_duration = "" play_duration = "" pause_duration = "" } # log.fields log.parsing_filters = { 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 ## ## 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 (subnode_exists('v.last_cs_bytes', c_client_id)) then ( cs_bytes_for_db = cs_bytes - node_value(subnode_by_name('v.last_cs_bytes', c_client_id)); 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; ); # echo("current cs_bytes: " . cs_bytes); # Remember the current cs_bytes value for a later line if (x_event eq "disconnect") then set_subnode_value('v.last_cs_bytes', c_client_id, 0); else set_subnode_value('v.last_cs_bytes', c_client_id, cs_bytes); # echo("cs_bytes_for_db: " . cs_bytes_for_db); # 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 ## ## 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). 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 (subnode_exists('v.last_sc_bytes', c_client_id)) then ( sc_bytes_for_db = sc_bytes - node_value(subnode_by_name('v.last_sc_bytes', c_client_id)); if (sc_bytes_for_db < 0) then ( sc_bytes_for_db = sc_bytes; ); ); # If there was no previous value, use sc_bytes else ( sc_bytes_for_db = sc_bytes; ); # Remember the current sc_bytes value for a later line if (x_event eq "disconnect") then set_subnode_value('v.last_sc_bytes', c_client_id, 0); else set_subnode_value('v.last_sc_bytes', c_client_id, sc_bytes); # In the database, the sc_bytes field should be the difference sc_bytes = sc_bytes_for_db; ); 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 ## ## 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 there was a previous value, use the difference in the database entry # if (subnode_exists('v.last_sc_stream_bytes', converted_stream_id)) then ( # sc_stream_bytes_for_db = sc_stream_bytes - node_value(subnode_by_name('v.last_sc_stream_bytes', converted_stream_id)); if (subnode_exists('v.last_sc_stream_bytes', x_stream_id)) then ( sc_stream_bytes_for_db = sc_stream_bytes - node_value(subnode_by_name('v.last_sc_stream_bytes', x_stream_id)); 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_subnode_value('v.last_sc_stream_bytes', converted_stream_id, 0); set_subnode_value('v.last_sc_stream_bytes', x_stream_id, 0); ); else ( # set_subnode_value('v.last_sc_stream_bytes', converted_stream_id, sc_stream_bytes); set_subnode_value('v.last_sc_stream_bytes', x_stream_id, 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 there was a previous value, use the difference in the database entry # if (subnode_exists('v.last_cs_stream_bytes', converted_stream_id)) then ( # cs_stream_bytes_for_db = cs_stream_bytes - node_value(subnode_by_name('v.last_cs_stream_bytes', converted_stream_id)); if (subnode_exists('v.last_cs_stream_bytes', x_stream_id)) then ( cs_stream_bytes_for_db = cs_stream_bytes - node_value(subnode_by_name('v.last_cs_stream_bytes', x_stream_id)); 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_subnode_value('v.last_cs_stream_bytes', converted_stream_id, 0); set_subnode_value('v.last_cs_stream_bytes', x_stream_id, 0); ); else ( # set_subnode_value('v.last_cs_stream_bytes', converted_stream_id, cs_stream_bytes); set_subnode_value('v.last_cs_stream_bytes', x_stream_id, 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_stream_durations ## fix_stream_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 (subnode_exists('v.last_stream_duration', converted_stream_id)) then ( # stream_duration_for_db = adj_duration - node_value(subnode_by_name('v.last_stream_duration', converted_stream_id)); if (subnode_exists('v.last_stream_duration', x_stream_id)) then ( stream_duration_for_db = adj_duration - node_value(subnode_by_name('v.last_stream_duration', x_stream_id)); if (stream_duration_for_db < 0.0) then ( stream_duration_for_db = adj_duration; ); ); # If there was no previous value, use adj_duration else ( stream_duration_for_db = adj_duration; ); #echo("### stream_duration_for_db=" . stream_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_subnode_value('v.last_stream_duration', converted_stream_id, 0.0); set_subnode_value('v.last_stream_duration', x_stream_id, 0.0); # set_subnode_value('v.last_stream_x_event', x_stream_id, ''); ); else ( # set_subnode_value('v.last_stream_duration', converted_stream_id, adj_duration); set_subnode_value('v.last_stream_duration', x_stream_id, adj_duration); # set_subnode_value('v.last_stream_x_event', x_stream_id, x_event); ); # # 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 ( # stream_duration_for_db = 0; # ); # In the database, the sc_bytes field should be the difference stream_duration = stream_duration_for_db/1000.0; # 2009-12-08 - KBB - set durations based on state if (node_value(subnode_by_name('v.last_stream_state', x_stream_id)) eq "playing") then ( play_duration = stream_duration; ); else if (node_value(subnode_by_name('v.last_stream_state', x_stream_id)) eq "paused") then ( pause_duration = stream_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_subnode_value('v.last_stream_state', x_stream_id, ''); ); else if ((x_event eq "play") or (x_event eq "unpause")) then ( set_subnode_value('v.last_stream_state', x_stream_id, 'playing'); ); else if (x_event eq "pause") then ( set_subnode_value('v.last_stream_state', x_stream_id, 'paused'); ); # else, leave it alone ); # if fix_stream_durations ` requires_fields = { c_client_id = true x_stream_id = true x_duration = true x_category = true x_event = true } } # fix_stream_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_subnode_value('v.stream_publish_time', x_stream_id, 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 = node_value(subnode_by_name('v.stream_publish_time', x_stream_id)); # 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 # stream_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 = { 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 = true requires_log_field = false } # wsessions = { # default = true # requires_log_field = true # log_field = "c_client_id" # type = "unique" # } # sessions 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 = "float" display_format_type = "bandwidth" } cs_bytes = { default = true type = "float" display_format_type = "bandwidth" } sc_stream_bytes = { default = true type = "float" display_format_type = "bandwidth" } cs_stream_bytes = { default = true type = "float" display_format_type = "bandwidth" } stream_duration = { default = true type = "float" display_format_type = "duration_compact" } # wsession_duration = { # default = true # type = "float" # display_format_type = "duration_compact" # } play_duration = { default = true type = "float" display_format_type = "duration_compact" } pause_duration = { default = false type = "float" display_format_type = "duration_compact" } publish_duration = { default = false type = "float" display_format_type = "duration_compact" } # duration_per_sessions = { # default = true # log_field = "wsession_duration" # requires_log_field = true # type = "float" # aggregation_method = "average" # average_denominator_field = "sessions" # display_format_type = duration_compact # } duration_per_stream = { default = true log_field = "stream_duration" requires_log_field = true type = "float" 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 = "" } # report_groups } # create_profile_wizard_options } # wowza_media_server_pro