Example #1
0
        private bool? ReadTail(string file, LogConfigurationElement log, string locationKey, ConcurrentBag<Metric> boomerangValues)
        {
            var rawValues = new ConcurrentDictionary<LogStatConfigurationElement, ConcurrentBag<Metric>>();

            if (log == null)
            {
                return null;
            }

            //If multiple log parsers use the same file, select the maximum size (including infinite)
            long maxTailMB = log.MaxTailMB;

            //Work out the last read position
            var info = new FileInfo(file);
            long offset;
            string hash = null;

            //Handle rolling single files
            if (log.SingleRollingFile)
            {
                //Read first line to compute hash
                var firstLineStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                try
                {
                    using (var reader = new StreamReader(firstLineStream))
                    {
                        firstLineStream = null;
                        if (reader.Peek() != -1)
                        {
                            var line = reader.ReadLine();
                            using (var md5 = MD5.Create())
                            {
                                hash = OffsetCursor<long>.GetMD5HashFileName(md5, line);
                            }
                        }
                    }
                }
                catch
                {
                }

                offset = cursor.GetLastRead(log.Name, file, hash);
            }
            else
            {
                offset = cursor.GetLastRead(log.Name, file);
            }

            long lastPosition = offset;

            //If file hasnt changed, don't bother opening
            if (info.Length <= offset)
            {
                return false;
            }

            //If the file is greater than our maxTailMB setting, skip to the maximum and proceed
            if (maxTailMB > 0)
            {
                long maxTailBytes = maxTailMB * 1048576;
                if (info.Length - offset > maxTailBytes)
                {
                    offset = info.Length - maxTailBytes;
                }
            }

            //Loop through the file doing matches
            var stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            try
            {
                using (var reader = new StreamReader(stream))
                {
                    stream = null;
                    reader.BaseStream.Seek(offset, SeekOrigin.Begin);
                    long lineCount = 0;

                    while (reader.Peek() != -1)
                    {
                        var line = reader.ReadLine();
                        lineCount++;

                        //Update the lastPosition counter before we try parse so any failures do not repeat the same line
                        lastPosition = reader.BaseStream.Position;
                        ParseLine(log, line, rawValues, boomerangValues, locationKey);

                        //Flush every 1000 lines to minimise memory footprint
                        if (lineCount >= 1000)
                        {
                            //send a debug metric
                            graphiteClient.SendQuickMetric("metrics.logLines.count", (int)lineCount);

                            //send real metrics to graphite
                            CollateAndSend(log, rawValues, file, lastPosition);
                            rawValues.Clear();
                            lineCount = 0;
                        }
                    }

                    //send a debug metric
                    graphiteClient.SendQuickMetric("metrics.logLines.count", (int)lineCount);
                }
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }
            }

            graphiteClient.SendQuickMetric("metrics.logLines.count", rawValues.Count);
            CollateAndSend(log, rawValues, file, lastPosition, hash);

            return true;
        }
Example #2
0
        private void GetBoomerangInformation(string value, ConcurrentBag<Metric> metrics, LogConfigurationElement log, DateTime timestamp, string userAgent)
        {
            var queryString = HttpUtility.ParseQueryString(value);

            //Try do an ip location lookup
            string locationKey = "{country}.{state}";
            if (!String.IsNullOrEmpty(queryString["user_ip"]))
            {
                if (queryString["user_ip"].StartsWith("10.55."))
                {
                    locationKey = locationKey.Replace("{country}", "local");
                    locationKey = locationKey.Replace("{state}", "local");
                }
                else
                {
                    try
                    {
                        var location = Geolocation.Instance.GetLocation(IPAddress.Parse(queryString["user_ip"]));

                        if (location != null && location.IpLocation != null)
                        {
                            locationKey = locationKey.Replace("{country}", String.IsNullOrWhiteSpace(location.IpLocation.Country) ? "unknown" : location.IpLocation.Country);
                            if (String.Compare(location.IpLocation.Country, "US", StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                locationKey = locationKey.Replace("{state}", String.IsNullOrWhiteSpace(location.IpLocation.State) ? "unknown" : location.IpLocation.State);
                            }
                        }
                    }
                    catch { }
                }
            }

            //If we didnt get a location then mark as unknown
            locationKey = locationKey.Replace("{country}", "unknown");
            locationKey = locationKey.Replace("{state}", "unknown");

            //Get the requested URL
            string url = queryString["u"];
            string urlKey = String.Empty;
            if (!String.IsNullOrEmpty(url))
            {
                //See if it is one of our seperate urls
                foreach (KeyValueConfigurationElement boomerangUrl in log.BoomerangUrls)
                {
                    if (Regex.IsMatch(url, boomerangUrl.Value, RegexOptions.IgnoreCase))
                    {
                        urlKey = boomerangUrl.Key;
                        break;
                    }
                }
            }

            //Try get the browser bersion
            string browserVersion = GetBrowserVersionFromUserAgent(userAgent);

            //Add counter for location
            metrics.Add(new Metric
                {
                    Key = "stats." + log.BoomerangKey + ".location." + locationKey + ".count",
                    Timestamp = timestamp,
                    Value = 1
                });

            //Counter with browser
            metrics.Add(new Metric
                {
                    Key = "stats." + log.BoomerangKey + ".browser." + browserVersion + ".count",
                    Timestamp = timestamp,
                    Value = 1
                });

            //Get start type
            string startKey = "unknown";
            if (!String.IsNullOrWhiteSpace(queryString["rt.start"]))
            {
                startKey = queryString["rt.start"];
            }

            foreach (string queryStringKey in queryString.Keys)
            {
                string fullKey = "timers." + log.BoomerangKey;
                if (String.Compare(queryStringKey, "t_other", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    var otherQueryString =
                        HttpUtility.ParseQueryString(
                            HttpUtility.UrlDecode(queryString[queryStringKey]).Replace("|", "=").Replace(",", "&"));
                    foreach (string otherKey in otherQueryString)
                    {
                        if ((otherKey != null) && (otherKey.StartsWith("t_")))
                        {
                            int otherValue;
                            if (Int32.TryParse(otherQueryString[otherKey], out otherValue))
                            {
                                //Timer by location
                                metrics.Add(new Metric
                                    {
                                        Key =
                                            fullKey + ".location." + locationKey + "." + otherKey.Replace("t_", "") +
                                            ".avg",
                                        Timestamp = timestamp,
                                        Value = otherValue
                                    });
                                //Timer by location & start type
                                metrics.Add(new Metric
                                    {
                                        Key =
                                            fullKey + ".location." + locationKey + "." + startKey + "." +
                                            otherKey.Replace("t_", "") + ".avg",
                                        Timestamp = timestamp,
                                        Value = otherValue
                                    });
                                //Timer by browser
                                metrics.Add(new Metric
                                    {
                                        Key =
                                            fullKey + ".browser." + browserVersion + "." +
                                            otherKey.Replace("t_", "") + ".avg",
                                        Timestamp = timestamp,
                                        Value = otherValue
                                    });
                                //Timer by browser & start type
                                metrics.Add(new Metric
                                    {
                                        Key =
                                            fullKey + ".browser." + browserVersion + "." + startKey + "." +
                                            otherKey.Replace("t_", "") + ".avg",
                                        Timestamp = timestamp,
                                        Value = otherValue
                                    });
                            }
                        }
                    }

                    continue;
                }

                if ((queryStringKey != null) && (queryStringKey.StartsWith("t_")))
                {
                    int otherValue;
                    if (Int32.TryParse(queryString[queryStringKey], out otherValue))
                    {
                        //Timer by location
                        metrics.Add(new Metric
                            {
                                Key =
                                    fullKey + ".location." + locationKey + "." + queryStringKey.Replace("t_", "") +
                                    ".avg",
                                Timestamp = timestamp,
                                Value = otherValue
                            });
                        //Timer by location & start type
                        metrics.Add(new Metric
                            {
                                Key =
                                    fullKey + ".location." + locationKey + "." + startKey + "." +
                                    queryStringKey.Replace("t_", "") + ".avg",
                                Timestamp = timestamp,
                                Value = otherValue
                            });
                        //Timer by browser
                        metrics.Add(new Metric
                            {
                                Key =
                                    fullKey + ".browser." + browserVersion + "." + queryStringKey.Replace("t_", "") +
                                    ".avg",
                                Timestamp = timestamp,
                                Value = otherValue
                            });
                        //Timer by browser & start type
                        metrics.Add(new Metric
                            {
                                Key =
                                    fullKey + ".browser." + browserVersion + "." + startKey + "." +
                                    queryStringKey.Replace("t_", "") + ".avg",
                                Timestamp = timestamp,
                                Value = otherValue
                            });

                        //------------
                        //TODO : Tidy up this huge mess
                        //------------
                        if ((!String.IsNullOrEmpty(urlKey))
                            &&
                            ((queryStringKey.Replace("t_", "") == "done") ||
                             (queryStringKey.Replace("t_", "") == "resp")))
                        {
                            //Timer by location & start type & page
                            metrics.Add(new Metric
                                {
                                    Key =
                                        fullKey + ".pages." + urlKey + ".location." + locationKey + "." + startKey +
                                        "." + queryStringKey.Replace("t_", "") + ".avg",
                                    Timestamp = timestamp,
                                    Value = otherValue
                                });
                            //Timer by browser & start type
                            metrics.Add(new Metric
                                {
                                    Key =
                                        fullKey + ".pages." + urlKey + ".browser." + browserVersion + "." + startKey +
                                        "." + queryStringKey.Replace("t_", "") + ".avg",
                                    Timestamp = timestamp,
                                    Value = otherValue
                                });
                        }
                    }
                }
            }
        }
Example #3
0
        private void ParseLine(LogConfigurationElement log, string line, ConcurrentDictionary<LogStatConfigurationElement, ConcurrentBag<Metric>> rawValues, ConcurrentBag<Metric> boomerangMetrics, string locationKey)
        {
            var matches = log.CompiledRegex.Matches(line);

            if (matches.Count > 0)
            {
                DateTime dateTime;
                try
                {
                    dateTime = DateTime.ParseExact(matches[0].Groups[log.Interval].Value,
                        log.DateFormat, CultureInfo.InvariantCulture, log.AssumeUniversal ? DateTimeStyles.AssumeUniversal : DateTimeStyles.AssumeLocal);
                    //Strip the last second
                    if (log.TenSecondGroup)
                    {
                        dateTime = dateTime.AddSeconds((dateTime.Second % 10) * -1);
                    }
                }
                catch (FormatException)
                {
                    //If we cant get the datetime then do nothing
                    return;
                }

                //Check if its inside the acceptable range
                if (dateTime < DateTime.Now.AddDays(log.MaxDaysToProcess * -1))
                {
                    return;
                }

                //Do boomerang calculations independetly;
                if (!String.IsNullOrWhiteSpace(log.BoomerangBeacon))
                {
                    //Check if this is a boomerang beacon
                    if (String.Compare(matches[0].Groups["url"].Value, log.BoomerangBeacon, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        GetBoomerangInformation(matches[0].Groups["querystring"].Value, boomerangMetrics, log, dateTime, matches[0].Groups["userAgent"].Value);
                    }
                }

                //Loop through all the various stats
                foreach (LogStatConfigurationElement stat in log.Stats)
                {
                    if ((stat.ExtensionsList.Count > 0) && (matches[0].Groups["url"] == null))
                    {
                        throw new InvalidOperationException("Stat extensions is not null, but no \"url\" is specified in the regex");
                    }

                    var key = stat.GraphiteKey.Replace("{locationKey}", locationKey);

                    //Check if there is an extensions filter
                    var found = false;
                    foreach (var extension in stat.ExtensionsList)
                    {
                        if (matches[0].Groups["url"].Value.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
                        {
                            found = true;
                            key = key.Replace("{extension}", extension.TrimStart('.'));
                        }
                    }

                    if (!String.IsNullOrWhiteSpace(stat.Match))
                    {
                        found =
                            String.Compare(matches[0].Groups["url"].Value, stat.Match,
                                           StringComparison.OrdinalIgnoreCase) == 0;
                    }
                    else if (!String.IsNullOrWhiteSpace(stat.Prefix))
                    {
                        found = matches[0].Groups["url"].Value.StartsWith(stat.Prefix, StringComparison.OrdinalIgnoreCase);
                    }

                    //If there are extension filters and they werent found, go to the next stat
                    if (((stat.ExtensionsList.Count > 0) || (!String.IsNullOrWhiteSpace(stat.Match)) || (!String.IsNullOrWhiteSpace(stat.Prefix))) && (!found))
                    {
                        continue;
                    }

                    //do key replacements
                    foreach (var map in log.Mapping.AllKeys)
                    {
                        if (log.Mapping[map].Value.StartsWith("?", StringComparison.Ordinal))
                        {
                            key = key.Replace("{" + map + "}",
                                              matches[0].Groups[log.Mapping[map].Value.TrimStart('?')].Value);
                        }
                        else
                        {
                            key = key.Replace("{" + map + "}", log.Mapping[map].Value);
                        }
                    }

                    try
                    {
                        //Create metric
                        var metric = new Metric
                                         {
                                             Key = key,
                                             Timestamp = dateTime
                                         };
                        if (!String.IsNullOrEmpty(stat.Value))
                        {
                            metric.Value = Int32.Parse(matches[0].Groups[stat.Value].Value, CultureInfo.InvariantCulture);
                        }

                        if (stat.IncludeZeros || (!stat.IncludeZeros && metric.Value > 0))
                        {
                            rawValues.TryAdd(stat, new ConcurrentBag<Metric>());
                            rawValues[stat].Add(metric);
                        }
                    }
                    catch
                    {
                        graphiteClient.SendQuickMetric("metrics.runTime.avg", 1);
                    }
                }
            }
        }
Example #4
0
        private void CollateAndSend(LogConfigurationElement log, IDictionary<LogStatConfigurationElement,
            ConcurrentBag<Metric>> rawValues, string file, long lastPosition, string hash = null)
        {
            var metrics = new List<Metric>();

            //Aggregate the metrics by their log aggregation method
            foreach (LogStatConfigurationElement stat in log.Stats)
            {
                if (rawValues.ContainsKey(stat))
                {

                    //Aggregate main value
                    switch (stat.AggregateType)
                    {
                        case "count":
                            metrics.AddRange(from value in rawValues[stat]
                                             group value by new { value.Timestamp, value.Key }
                                                 into metricGroup
                                                 select
                                                     new Metric
                                                     {
                                                         Key = metricGroup.Key.Key,
                                                         Timestamp = metricGroup.Key.Timestamp,
                                                         Value = metricGroup.Count()
                                                     });
                            break;
                        case "max":
                            metrics.AddRange(from value in rawValues[stat]
                                             group value by new { value.Timestamp, value.Key }
                                                 into metricGroup
                                                 select
                                                     new Metric
                                                     {
                                                         Key = metricGroup.Key.Key,
                                                         Timestamp = metricGroup.Key.Timestamp,
                                                         Value =
                                                             metricGroup.Max(metric => metric.Value)
                                                     });
                            break;
                        case "min":
                            metrics.AddRange(from value in rawValues[stat]
                                             group value by new { value.Timestamp, value.Key }
                                                 into metricGroup
                                                 select
                                                     new Metric
                                                     {
                                                         Key = metricGroup.Key.Key,
                                                         Timestamp = metricGroup.Key.Timestamp,
                                                         Value =
                                                             metricGroup.Min(metric => metric.Value)
                                                     });
                            break;
                        default:
                            metrics.AddRange(from value in rawValues[stat]
                                             group value by new { value.Timestamp, value.Key }
                                                 into metricGroup
                                                 select
                                                     new Metric
                                                     {
                                                         Key = metricGroup.Key.Key,
                                                         Timestamp = metricGroup.Key.Timestamp,
                                                         Value =
                                                             metricGroup.Sum(metric => metric.Value) /
                                                             metricGroup.Count()
                                                     });
                            break;
                    }

                    //Add per second calulcation if required
                    if ((stat.CalculatePerSecond) && (log.TenSecondGroup))
                    {
                        metrics.AddRange(from value in rawValues[stat]
                                         group value by new { value.Timestamp, value.Key }
                                             into metricGroup
                                             select
                                                 new Metric
                                                 {
                                                     Key = metricGroup.Key.Key + "PerSecond",
                                                     Timestamp = metricGroup.Key.Timestamp,
                                                     Value = metricGroup.Count() / 10
                                                 });

                    }
                }
            }

            cursor.StoreLastRead(log.Name, file, lastPosition, hash);
            graphiteClient.SendMetrics(metrics);
        }