# Copyright (c) 2010 Flowerfire, Inc. All Rights Reserved. edgecast_flash_media_server = { plugin_version = "1.0" info.1.manfacturer = "Edgecast" info.1.device = "Flash Media Server" info.1.version = "" # 2011-03-01 - KBB - Created initial plug-in, based on flash_media_server.cfg # The name of the log format log.format.format_label = "Edgecast Flash Media Server Log Format" log.miscellaneous.log_data_type = "generic_w3c" log.miscellaneous.log_format_type = "web_server" # The EdgeCast log is a Flash log, but it has no #Software, or any line above #Fields. # Since this is provided to their clients by EdgeCast, we should be safe in assuming that the # order of fields will be fairly consistent among users, though EdgeCast may change it. log.format.autodetect_expression = ` if (matches_regular_expression(volatile.log_data_line, "^#Software: ")) then ( v.seen_software_in_header = true; false; ); # If we see a line like this before the #Software line, assume EdgeCast. #Fields: x-event x-category date time x-duration x-status c-ip s-ip c-proto cs-uri-stem cs-uri-query c-referrer c-user-agent cs-bytes sc-bytes x-file-size x-file-length x-sname x-file-name x-file-ext x-app sc-stream-bytes c-client-id x-sid x-comment else if (!node_exists('v.seen_software_in_header') and matches_regular_expression(volatile.log_data_line, "^#Fields: x-event x-category.*x-comment$")) then ( true; ); else false; ` log.format.collected_entry_lifespan = 10000 auto_setup.omit_database_fields = "x_duration" # Literal apostrophes can appear in field values, and should not be treated as quotes log.format.treat_apostrophes_as_quotes = false log.format.date_format = "auto" log.format.time_format = "auto" # Use single-process builds for profiles based on this plug-in, because the filter code requires in-order log data to get correct results. log.processing.distributed.method = "1" # Don't time out sessions, 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.fields = { session_events = "" visitor_id = "" } database.fields = { visitor_id = "" } # database.fields # From http://livedocs.adobe.com/fms/2/docs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000181.html # and http://help.adobe.com/en_US/FlashMediaServer/3.5_AdminGuide/WS5b3ccc516d4fbf351e63e3d119f2926bcf-79d1.html # cs-bytes - This field shows the number of bytes transferred from the client to the server. # This information can be used to bill customers per session. To calculate the bandwidth # usage per session, subtract the 'cs-bytes' in the 'connect' event from the 'cs-bytes' in # the 'disconnect' event. # sc-bytes - This field shows the number of bytes transferred from the server to the client. # This information can be used to bill customers per session. To calculate the bandwidth # usage per session, subtract the 'sc-bytes' in the 'connect' event by the 'sc-bytes' in # the 'disconnect' event # cs-stream-bytes - This field shows the number of bytes transferred from the client to the server # per stream. To calculate the bandwidth usage per stream, subtract the 'cs-stream-bytes' # in the 'publish' event by the 'cs-stream-bytes' in the 'unpublish' event. # sc-stream-bytes - This field shows the number of bytes transferred from the server to the client per # stream. To calculate the bandwidth usage per stream, subtract the 'sc-stream-bytes' # in the 'play' event by the 'sc-stream-bytes' in the 'stop' event. log.filter_initialization = ` v.last_cs_bytes = ""; v.last_sc_bytes = ""; v.last_cs_stream_bytes = ""; v.last_sc_stream_bytes = ""; float cs_bytes_for_db; float sc_bytes_for_db; float cs_stream_bytes_for_db; float sc_stream_bytes_for_db; ` log.parsing_filters = { # Logs will have c_ip or c_client_id or both. Set visitor_id, used in other filters # based on whichever is available. c_client_id will be used if both exist. set_visitor_to_c_ip = { value = ` visitor_id = replace_all(c_ip, '.', '_'); ` requires_fields = { c_ip = true } } # set_visitor_to_c_ip # Keep this filter 2nd. c_client_id is the more precise id. set_visitor_to_c_client_id = { value = ` visitor_id = c_client_id; ` requires_fields = { c_client_id = true } } # set_visitor_to_c_client_id # 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). fix_cs_bytes = { value = ` #disconnect session 2011-03-23 16:42:09 332 200 66.166.246.66 76.66.66.66 rtmp rtmp://fms.cdn.net:443/lizard/Flashcontent - http://www.lizard.com/video/flvplayer.swf?3323 WIN 10,1,85,3 3223 886058 - - - - - lizard - 5711489310709610343 - - if (visitor_id ne "(empty)" and visitor_id ne "-") then ( # If there was a previous value, use the difference in the database entry v.last_cs_bytes = get_collected_field(visitor_id, 'last_cs_bytes'); #if (subnode_exists('v.last_cs_bytes', visitor_id)) then ( if (v.last_cs_bytes ne '') then ( # If value is negative due to logging bug (above), then set it to the previous # value as if there have been no bytes. The result is the same as setting it # to zero if the x-event is disconnect as it is in all examples seen so far. if (cs_bytes < 0) then cs_bytes = v.last_cs_bytes; cs_bytes_for_db = 0.0 + cs_bytes - v.last_cs_bytes; if (cs_bytes_for_db < 0) then ( cs_bytes_for_db = cs_bytes; ); ); else ( if (cs_bytes < 0) then cs_bytes = 0; # This compensates for a logging bug. cs_bytes_for_db = cs_bytes; ); # Remember the current cs_bytes value for a later event for this visitor if (x_event eq "disconnect") then ( #set_subnode_value('v.last_cs_bytes', visitor_id, 0); set_collected_field(visitor_id, 'last_cs_bytes', 0); ); else ( #set_subnode_value('v.last_cs_bytes', visitor_id, cs_bytes); set_collected_field(visitor_id, 'last_cs_bytes', cs_bytes); ); # In the database, the cs_bytes field should be the difference cs_bytes = cs_bytes_for_db; ); # if visitor_id else ( cs_bytes = 0; ); ` requires_fields = { cs_bytes = true x_event = true } } # fix_cs_bytes # 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). fix_sc_bytes = { value = ` if (visitor_id ne "(empty)" and visitor_id ne "-") then ( # If there was a previous value, use the difference in the database entry v.last_sc_bytes = get_collected_field(visitor_id, 'last_sc_bytes'); #if (subnode_exists('v.last_sc_bytes', visitor_id)) then ( if (v.last_sc_bytes ne '') then ( # Compensates for a logging bug - seen only with cs-bytes so far. if (sc_bytes < 0) then sc_bytes = v.last_sc_bytes; #sc_bytes_for_db = 0.0 + sc_bytes - node_value(subnode_by_name('v.last_sc_bytes', visitor_id)); sc_bytes_for_db = 0.0 + sc_bytes - v.last_sc_bytes; if (sc_bytes_for_db < 0) then ( sc_bytes_for_db = sc_bytes; ); ); else ( if (sc_bytes < 0) then sc_bytes = 0; # Compensates for a logging bug - seen only with cs-bytes so far. sc_bytes_for_db = sc_bytes; ); # Remember the current sc_bytes value for a later event for this visitor if (x_event eq "disconnect") then ( #set_subnode_value('v.last_sc_bytes', visitor_id, 0); set_collected_field(visitor_id, 'last_sc_bytes', 0); ); else ( #set_subnode_value('v.last_sc_bytes', visitor_id, sc_bytes); set_collected_field(visitor_id, 'last_sc_bytes', sc_bytes); ); # In the database, the sc_bytes field should be the difference sc_bytes = sc_bytes_for_db; ); # if visitor_id else ( sc_bytes = 0; ); ` requires_fields = { sc_bytes = true x_event = true } } # fix_sc_bytes # Fix the sc_stream_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). fix_sc_stream_bytes = { value = ` if (visitor_id ne "(empty)" and visitor_id ne "-") then ( # If there was a previous value, use the difference in the database entry v.last_sc_stream_bytes = get_collected_field(visitor_id, 'last_sc_stream_bytes'); #if (subnode_exists('v.last_sc_stream_bytes', visitor_id)) then ( if (v.last_sc_stream_bytes ne '') then ( # Compensates for a logging bug - seen only with cs-bytes so far. if (sc_stream_bytes < 0) then sc_stream_bytes = v.last_sc_stream_bytes; #sc_stream_bytes_for_db = 0.0 + sc_stream_bytes - node_value(subnode_by_name('v.last_sc_stream_bytes', visitor_id)); sc_stream_bytes_for_db = 0.0 + sc_stream_bytes - v.last_sc_stream_bytes; if (sc_stream_bytes_for_db < 0) then ( sc_stream_bytes_for_db = sc_stream_bytes; ); ); else ( # Compensates for a logging bug - seen only with cs-bytes so far. if (sc_stream_bytes < 0) then sc_stream_bytes = 0; sc_stream_bytes_for_db = sc_stream_bytes; ); # Remember the current sc_stream_bytes value for a later event for this visitor if (x_event eq "stop") then ( #set_subnode_value('v.last_sc_stream_bytes', visitor_id, 0); set_collected_field(visitor_id, 'last_sc_stream_bytes', 0); ); else ( #set_subnode_value('v.last_sc_stream_bytes', visitor_id, sc_stream_bytes); set_collected_field(visitor_id, 'last_sc_stream_bytes', sc_stream_bytes); ); # In the database, the sc_stream_bytes field should be the difference sc_stream_bytes = sc_stream_bytes_for_db; ); # if visitor_id else ( sc_stream_bytes = 0; ); ` requires_fields = { sc_stream_bytes = true x_event = true } } # fix_sc_stream_bytes # Fix the cs_stream_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). fix_cs_stream_bytes = { value = ` if (visitor_id ne "(empty)" and visitor_id ne "-") then ( # If there was a previous value, use the difference in the database entry v.last_cs_stream_bytes = get_collected_field(visitor_id, 'last_cs_stream_bytes'); #if (subnode_exists('v.last_cs_stream_bytes', visitor_id)) then ( if (v.last_cs_stream_bytes ne '') then ( # Compensates for a logging bug - seen only with cs-bytes so far. if (cs_stream_bytes < 0) then cs_stream_bytes = v.last_cs_stream_bytes; #cs_stream_bytes_for_db = 0.0 + cs_stream_bytes - node_value(subnode_by_name('v.last_cs_stream_bytes', visitor_id)); cs_stream_bytes_for_db = 0.0 + cs_stream_bytes - v.last_cs_stream_bytes; if (cs_stream_bytes_for_db < 0) then ( cs_stream_bytes_for_db = cs_stream_bytes; ); ); else ( # Compensates for a logging bug - seen only with cs-bytes so far. if (cs_stream_bytes < 0) then cs_stream_bytes = 0; cs_stream_bytes_for_db = cs_stream_bytes; ); # Remember the current cs_stream_bytes value for a later event for this visitor if (x_event eq "unpublish") then ( #set_subnode_value('v.last_cs_stream_bytes', visitor_id, 0); set_collected_field(visitor_id, 'last_cs_stream_bytes', 0); ); else ( #set_subnode_value('v.last_cs_stream_bytes', visitor_id, cs_stream_bytes); set_collected_field(visitor_id, 'last_cs_stream_bytes', cs_stream_bytes); ); # In the database, the cs_stream_bytes field should be the difference cs_stream_bytes = cs_stream_bytes_for_db; ); # if visitor_id else ( cs_stream_bytes = 0; ); ` requires_fields = { cs_stream_bytes = true x_event = true } } # fix_cs_stream_bytes set_duration = { value = ` if ((x_event eq "stop") and (x_category eq "stream")) then stream_duration = x_duration; else if ((x_event eq "disconnect") and (x_category eq "session")) then flash_session_duration = x_duration; ` requires_fields = { x_duration = true x_event = true x_category = true stream_duration = true } } # set_duration set_file_size = { value = ` if (!((x_event eq "stop") and (x_category eq "stream"))) then x_file_size = 0; ` requires_fields = { x_file_size = true x_event = true x_category = true } } # set_file_size set_file_length = { value = ` if (!((x_event eq "stop") and (x_category eq "stream"))) then x_file_length = 0; ` requires_fields = { x_file_length = true x_event = true x_category = true } } # set_file_length # set x-sname to (empty) when '-' for session connect/disconnect lines # so it does not appear in the stream name report as it is only session traffic set_stream_name = { value = `if (x_sname eq '-') then x_sname = '(empty)';` requires_fields = { x_sname = true } # requires_fields } # set_stream_name set_session_event = { value = ` if ((x_event eq 'play' or x_event eq 'stop') and x_category eq "stream" and visitor_id ne '-' and visitor_id ne '(empty)') then ( session_events = 1; ); ` requires_fields = { x_duration = true visitor_id = true x_event = true x_category = true } # requires_fields } # set_session_event } # log.parsing_filters # log.parsing_filters.debug = ` #echo("----------------------------"); #echo(cs_uri_stem); #echo(x_event); #echo(x_category); #echo(session_events); #echo(visitor_id); #` 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.numerical_fields = { events = { default = true requires_log_field = false } session_events = { default = true } # session_events unique_client_ips = { default = false requires_log_field = true log_field = "c_ip" type = "unique" } # unique_client_ips sc_bytes = { type = "float" display_format_type = "bandwidth" } cs_bytes = { type = "float" display_format_type = "bandwidth" } sc_stream_bytes = { type = "float" display_format_type = "bandwidth" } cs_stream_bytes = { type = "float" display_format_type = "bandwidth" } x_file_size = { type = "float" display_format_type = "bandwidth" } x_file_length = { type = "float" display_format_type = "duration_compact" } # KBB - 2011-02-26 - This field is separated into stream and session duration. # Aggregating the value is misleading. # x_duration = { # default = false # requires_log_field = false # type = "float" # display_format_type = "duration_compact" # } stream_duration = { default = false requires_log_field = false type = "float" display_format_type = "duration_compact" } flash_session_duration = { default = false requires_log_field = false type = "float" display_format_type = "duration_compact" } # flash_session_duration watch_duration_percentage = { default = false log_field = "stream_duration" requires_log_field = true type = "float" aggregation_method = "average" average_denominator_field = "x_file_length" display_format_type = two_digit_fixed } bytes_watched_percentage = { default = false log_field = "sc_stream_bytes" requires_log_field = true type = "float" aggregation_method = "average" average_denominator_field = "x_file_size" display_format_type = two_digit_fixed } stream_duration_per_client_ip = { default = false log_field = "stream_duration" requires_log_field = true type = "float" aggregation_method = "average" average_denominator_field = "unique_client_ips" display_format_type = duration_compact } } # database.numerical_fields log.field_options = { sessions_page_field = "cs_uri_stem" sessions_id_field = "visitor_id" sessions_visitor_id_field = "c_ip" sessions_event_field = "session_events" } # log.field_options create_profile_wizard_options = { # How the reports should be grouped in the report menu report_groups = { date_time_group = "" content_group = { cs_uri_stem = true cs_uri_query = true #file_type = true s_uri = true x_ctx = true x_sname = true x_sname_query = true x_suri_query = true x_suri_stem = true x_suri = true x_spos = true x_file_ext = true x_file_name = true } # content_group client_group = { c_client_id = true c_ip = true c_connect_type = true c_proto = true c_referrer = true c_user_agent = true domain = true domain_description = true isp = true location = true organization = true screen_depth = true screen_dimensions = true } # client_group other_group = { s_ip = true x_adaptor = true x_app = true x_appinst = true x_category = true x_comment = true x_cpu_load = true x_event = true x_mem_load = true x_pid = true x_sid = true x_sc_qos_bytes = true x_service_name = true x_status = true x_vhost = true tz = true } # other_group } # report_groups final_step = ` include "templates.admin.profiles.setup_reports_util"; string profile = "profiles." . volatile.new_profile_name; string reports = profile . ".statistics.reports"; string summary = profile . ".statistics.reports.single_page_summary.report_elements"; string menu = profile . ".statistics.reports_menu"; # Create the standard reports add_standard_reports(profile); # Remove unneeded reports delete_node(reports . ".visitor_id"); delete_node(summary . ".visitor_id"); delete_node(menu . ".visitor_id"); delete_node(reports . ".worm"); delete_node(summary . ".worm"); delete_node(menu . ".worm"); # The x_file_ext field replaces this. delete_node(reports . ".file_type"); delete_node(summary . ".file_type"); delete_node(menu . ".file_type"); ` } # create_profile_wizard_options } # edgecast_flash_media_server