Beispiel #1
0
        protected override void OnEventSourceCreated(EventSource eventSource)
        {
            // There is a possible race where we can get notified of the creation of our own internal logger during
            // construction of the log manager. The LogManager constructor helps handle this by explicitly calling
            // this function once it is safe to do so.
            if (eventSource is InternalLogger && InternalLogger.Write == null)
            {
                return;
            }

            lock (this.eventSourceInfosLock)
            {
                if (!this.eventSourceInfos.ContainsKey(eventSource))
                {
                    var updatedData = new EventSourceInfo(eventSource);
                    this.eventSourceInfos[eventSource] = updatedData;
                    InternalLogger.Write.NewEventSource(eventSource.Name, eventSource.Guid);
                }
                else
                {
                    return; // we should have already done the work for this source.
                }
            }
            lock (this.loggersLock)
            {
                if (this.logConfigurations != null)
                {
                    foreach (var kvp in this.logConfigurations)
                    {
                        LogConfiguration config = kvp.Value;
                        // We need to update our loggers any time a config shows up where they had a dependency
                        // that probably wasn't resolved. This will be the case either when it was a named source
                        // or it was a GUID source on a type that can't directly subscribe to GUIDs (i.e. not an ETW
                        // trace session)
                        IEventLogger logger = null;
                        switch (config.FileType)
                        {
                        case LoggerType.Console:
                            logger = this.consoleLogger;
                            break;

                        case LoggerType.Network:
                            logger = this.GetNamedNetworkLogger(kvp.Key);
                            break;

                        default:
                            logger = this.GetNamedFileLogger(kvp.Key).Logger;
                            break;
                        }
                        LogSourceLevels levels;
                        if (config.NamedSources.TryGetValue(eventSource.Name, out levels) ||
                            (!config.HasFeature(LogConfiguration.Features.GuidSubscription) &&
                             config.GuidSources.TryGetValue(eventSource.Guid, out levels)))
                        {
                            ApplyConfigForEventSource(config, logger, eventSource, levels);
                        }
                    }
                }
            }
        }
Beispiel #2
0
        private static bool ParseLogFilters(XmlNode xmlNode, LogConfiguration config)
        {
            bool clean = true;

            foreach (XmlNode source in xmlNode.SelectNodes(LogFilterTag))
            {
                string filterValue = source.InnerText.Trim();
                if (string.IsNullOrEmpty(filterValue))
                {
                    InternalLogger.Write.InvalidConfiguration("empty/invalid filter value");
                    clean = false;
                    continue;
                }

                if (config.Filters.Contains(filterValue))
                {
                    InternalLogger.Write.InvalidConfiguration("duplicate filter value " + filterValue);
                    clean = false;
                    continue;
                }

                config.Filters.Add(filterValue);
            }

            return(clean);
        }
 internal void Merge(LogConfiguration otherLog)
 {
     foreach (var sub in otherLog.subscriptions)
     {
         this.AddSubscription(sub);
     }
     foreach (var filter in otherLog.Filters)
     {
         this.AddFilter(filter);
     }
 }
Beispiel #4
0
 private static void ApplyConfigForEventSource(LogConfiguration config, IEventLogger logger, EventSource source,
                                               LogSourceLevels levels)
 {
     if (config.HasFeature(LogConfiguration.Features.EventSourceSubscription))
     {
         logger.SubscribeToEvents(source, levels.Level, levels.Keywords);
     }
     else if (config.HasFeature(LogConfiguration.Features.GuidSubscription))
     {
         logger.SubscribeToEvents(source.Guid, levels.Level, levels.Keywords);
     }
 }
        private static bool ParseLogNode(XmlNode xmlNode, LogConfiguration config)
        {
            var clean = true;

            foreach (XmlAttribute logAttribute in xmlNode.Attributes)
            {
                try
                {
                    switch (logAttribute.Name.ToLower(CultureInfo.InvariantCulture))
                    {
                    case LogBufferSizeAttribute:
                        config.BufferSizeMB = int.Parse(logAttribute.Value);
                        break;

                    case LogDirectoryAttribute:
                        config.Directory = logAttribute.Value;
                        break;

                    case LogFilenameTemplateAttribute:
                        config.FilenameTemplate = logAttribute.Value;
                        break;

                    case LogTimestampLocal:
                        config.TimestampLocal = bool.Parse(logAttribute.Value);
                        break;

                    case LogRotationAttribute:
                        config.RotationInterval = int.Parse(logAttribute.Value);
                        break;

                    case LogHostnameAttribute:
                        config.Hostname = logAttribute.Value;
                        break;

                    case LogPortAttribute:
                        config.Port = ushort.Parse(logAttribute.Value);
                        break;
                    }
                }
                catch (Exception e) when(e is FormatException || e is OverflowException)
                {
                    InternalLogger.Write.InvalidConfiguration($"Attribute {logAttribute.Name} has invalid value {logAttribute.Value} ({e.GetType()}: {e.Message})");
                    clean = false;
                }
            }

            return(clean);
        }
            public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
                                            JsonSerializer serializer)
            {
                var     jObject = JObject.Load(reader);
                JToken  token;
                string  name = null;
                LogType type = LogType.None;

                if (jObject.TryGetValue(NameProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    name = token.Value <string>();
                }
                if (jObject.TryGetValue(TypeProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    type = token.Value <string>().ToLogType();
                }

                var sourcesArray = jObject.GetValue(SourcesProperty, StringComparison.OrdinalIgnoreCase);
                var sources      = serializer.Deserialize <List <EventProviderSubscription> >(sourcesArray.CreateReader());

                var filtersArray = jObject.GetValue(FiltersProperty, StringComparison.OrdinalIgnoreCase);
                var filters      = filtersArray != null && filtersArray.HasValues
                                  ? serializer.Deserialize <List <string> >(filtersArray.CreateReader())
                                  : new List <string>();

                var log = new LogConfiguration(name, type, sources, filters);

                if (jObject.TryGetValue(BufferSizeMBProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.BufferSizeMB = token.Value <int>();
                }
                if (jObject.TryGetValue(DirectoryProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.Directory = token.Value <string>();
                }
                if (jObject.TryGetValue(FilenameTemplateProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.FilenameTemplate = token.Value <string>();
                }
                if (jObject.TryGetValue(TimestampLocalProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.TimestampLocal = token.Value <bool>();
                }
                if (jObject.TryGetValue(RotationIntervalProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.RotationInterval = token.Value <int>();
                }
                if (jObject.TryGetValue(MaximumAgeProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.MaximumAge = token.Value <TimeSpan>();
                }
                if (jObject.TryGetValue(MaximumSizeProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.MaximumSize = token.Value <int>();
                }
                if (jObject.TryGetValue(HostnameProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.Hostname = token.Value <string>();
                }
                if (jObject.TryGetValue(PortProperty, StringComparison.OrdinalIgnoreCase, out token))
                {
                    log.Port = token.Value <ushort>();
                }

                return(log);
            }
 private bool Equals(LogConfiguration other)
 {
     return(string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase));
 }
Beispiel #8
0
        private static bool ParseLogSources(XmlNode xmlNode, LogConfiguration config)
        {
            bool clean = true;

            foreach (XmlNode source in xmlNode.SelectNodes(SourceTag))
            {
                string sourceName     = null;
                Guid   sourceProvider = Guid.Empty;
                var    sourceLevel    = EventLevel.Informational;
                var    sourceKeywords = (long)EventKeywords.None;
                foreach (XmlAttribute sourceAttribute in source.Attributes)
                {
                    switch (sourceAttribute.Name.ToLower(CultureInfo.InvariantCulture))
                    {
                    case SourceKeywordsAttribute:
                        // Yes, really. The .NET integer TryParse methods will get PISSED if they see 0x in front of
                        // hex digits. Dumb hack is dumb.
                        string value = sourceAttribute.Value.Trim();
                        if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                        {
                            value = value.Substring(2);
                        }

                        if (!long.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                           out sourceKeywords))
                        {
                            InternalLogger.Write.InvalidConfiguration("invalid keywords value " + sourceAttribute.Value);
                            clean = false;
                        }
                        break;

                    case SourceMinSeverityAttribute:
                        if (!Enum.TryParse(sourceAttribute.Value, true, out sourceLevel))
                        {
                            InternalLogger.Write.InvalidConfiguration("invalid severity value " + sourceAttribute.Value);
                            clean = false;
                        }
                        break;

                    case SourceProviderIDAttribute:
                        if (!Guid.TryParse(sourceAttribute.Value, out sourceProvider))
                        {
                            InternalLogger.Write.InvalidConfiguration("invalid providerID GUID " + sourceAttribute.Value);
                            clean = false;
                        }
                        break;

                    case SourceProviderNameAttribute:
                        sourceName = sourceAttribute.Value.Trim();
                        break;
                    }
                }

                var levels = new LogSourceLevels(sourceLevel, (EventKeywords)sourceKeywords);
                if (sourceProvider != Guid.Empty)
                {
                    config.GuidSources[sourceProvider] = levels;
                }
                else if (!string.IsNullOrEmpty(sourceName))
                {
                    config.NamedSources[sourceName] = levels;
                }
                else
                {
                    InternalLogger.Write.InvalidConfiguration("source has neither name nor guid");
                    clean = false;
                }
            }

            return(clean);
        }
Beispiel #9
0
        private static bool ParseLogNode(XmlNode xmlNode, LogConfiguration config)
        {
            bool clean = true;

            foreach (XmlAttribute logAttribute in xmlNode.Attributes)
            {
                switch (logAttribute.Name.ToLower(CultureInfo.InvariantCulture))
                {
                case LogBufferSizeAttribute:
                    if (!int.TryParse(logAttribute.Value, out config.BufferSize) ||
                        !IsValidFileBufferSize(config.BufferSize))
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid buffer size " + logAttribute.Value);
                        config.BufferSize = DefaultFileBufferSizeMB;
                        clean             = false;
                    }
                    break;

                case LogDirectoryAttribute:
                    if (!IsValidDirectory(logAttribute.Value))
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid directory name " + logAttribute.Value);
                        clean = false;
                    }
                    else
                    {
                        config.Directory = logAttribute.Value;
                    }
                    break;

                case LogFilenameTemplateAttribute:
                    if (!FileBackedLogger.IsValidFilenameTemplate(logAttribute.Value))
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid filename template " + logAttribute.Value);
                        clean = false;
                    }
                    else
                    {
                        config.FilenameTemplate = logAttribute.Value;
                    }
                    break;

                case LogTimestampLocal:
                    if (!bool.TryParse(logAttribute.Value, out config.TimestampLocal))
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid timestamplocal value " + logAttribute.Value);
                        config.TimestampLocal = false;
                        clean = false;
                    }
                    break;

                case LogRotationAttribute:
                    if (!int.TryParse(logAttribute.Value, out config.RotationInterval) ||
                        !IsValidRotationInterval(config.RotationInterval))
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid rotation interval " + logAttribute.Value);
                        config.RotationInterval = DefaultRotationInterval;
                        clean = false;
                    }
                    break;

                case LogHostnameAttribute:
                    if (Uri.CheckHostName(logAttribute.Value) == UriHostNameType.Unknown)
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid hostname name " + logAttribute.Value);
                        clean = false;
                    }
                    else
                    {
                        config.Hostname = logAttribute.Value;
                    }
                    break;

                case LogPortAttribute:
                    if (!int.TryParse(logAttribute.Value, out config.Port) ||
                        config.Port <= 0)
                    {
                        InternalLogger.Write.InvalidConfiguration("invalid port " + logAttribute.Value);
                        config.Port = 80;
                        clean       = false;
                    }
                    break;
                }
            }

            clean &= ParseLogSources(xmlNode, config);
            clean &= ParseLogFilters(xmlNode, config);
            return(clean);
        }
Beispiel #10
0
        private bool ApplyConfiguration()
        {
            var newConfig = new Dictionary <string, LogConfiguration>(StringComparer.OrdinalIgnoreCase);

            if (!ParseConfiguration(this.configurationFileData, newConfig) ||
                !ParseConfiguration(this.configurationData, newConfig))
            {
                return(false);
            }

            lock (this.loggersLock)
            {
                foreach (var logger in this.fileLoggers.Values)
                {
                    logger.Dispose();
                }
                foreach (var logger in this.networkLoggers.Values)
                {
                    logger.Dispose();
                }
                this.fileLoggers.Clear();
                this.networkLoggers.Clear();
                this.logConfigurations = newConfig;

                foreach (var kvp in this.logConfigurations)
                {
                    string           loggerName   = kvp.Key;
                    LogConfiguration loggerConfig = kvp.Value;
                    IEventLogger     logger;
                    if (loggerConfig.FileType == LoggerType.Console)
                    {
                        // We re-create the console logger to clear its config (since we don't have a better way
                        // to do that right now).
                        this.CreateConsoleLogger();
                        logger = this.consoleLogger;
                    }
                    else if (loggerConfig.FileType == LoggerType.Network)
                    {
                        logger = this.CreateNetLogger(loggerName, loggerConfig.Hostname, loggerConfig.Port);
                    }
                    else
                    {
                        logger = this.CreateFileLogger(loggerConfig.FileType, loggerName, loggerConfig.Directory,
                                                       loggerConfig.BufferSize, loggerConfig.RotationInterval,
                                                       loggerConfig.FilenameTemplate,
                                                       loggerConfig.TimestampLocal);
                    }

                    foreach (var f in loggerConfig.Filters)
                    {
                        logger.AddRegexFilter(f);
                    }

                    // Build a collection of all desired subscriptions so that we can subscribe in bulk at the end.
                    // We do this because ordering may matter to specific types of loggers and they are best suited to
                    // manage that internally.
                    var subscriptions = new List <EventProviderSubscription>();
                    foreach (var ns in loggerConfig.NamedSources)
                    {
                        EventSourceInfo sourceInfo;
                        string          name   = ns.Key;
                        LogSourceLevels levels = ns.Value;
                        if ((sourceInfo = GetEventSourceInfo(name)) != null)
                        {
                            subscriptions.Add(new EventProviderSubscription(sourceInfo.Source)
                            {
                                MinimumLevel = levels.Level,
                                Keywords     = levels.Keywords
                            });
                        }
                    }

                    foreach (var gs in loggerConfig.GuidSources)
                    {
                        EventSourceInfo sourceInfo;
                        Guid            guid   = gs.Key;
                        LogSourceLevels levels = gs.Value;
                        if (loggerConfig.HasFeature(LogConfiguration.Features.GuidSubscription))
                        {
                            subscriptions.Add(new EventProviderSubscription(guid)
                            {
                                MinimumLevel = levels.Level,
                                Keywords     = levels.Keywords
                            });
                        }
                        else if (loggerConfig.HasFeature(LogConfiguration.Features.EventSourceSubscription) &&
                                 (sourceInfo = GetEventSourceInfo(guid)) != null)
                        {
                            subscriptions.Add(new EventProviderSubscription(sourceInfo.Source)
                            {
                                MinimumLevel = levels.Level,
                                Keywords     = levels.Keywords
                            });
                        }
                    }

                    logger.SubscribeToEvents(subscriptions);
                }
            }

            return(true);
        }
Beispiel #11
0
        private static bool ParseConfiguration(string configurationXml, Dictionary <string, LogConfiguration> loggers)
        {
            bool clean = true; // used to track whether any errors were encountered

            if (string.IsNullOrEmpty(configurationXml))
            {
                return(true); // it's okay to have nothing at all
            }

            var configuration = new XmlDocument();

            try
            {
                configuration.LoadXml(configurationXml);
            }
            catch (XmlException)
            {
                InternalLogger.Write.InvalidConfiguration("Configuration was not valid XML");
                return(false);
            }

            XmlNode node = configuration.SelectSingleNode(EtwOverrideXpath);

            if (node != null)
            {
                XmlNode setting   = node.Attributes.GetNamedItem(EtwOverrideEnabledAttribute);
                bool    isEnabled = (AllowEtwLogging == AllowEtwLoggingValues.Enabled);
                if (setting == null || !bool.TryParse(setting.Value, out isEnabled))
                {
                    InternalLogger.Write.InvalidConfiguration(EtwOverrideXpath + " tag has invalid " +
                                                              EtwOverrideEnabledAttribute + " attribute");
                    clean = false;
                }

                AllowEtwLogging = isEnabled ? AllowEtwLoggingValues.Enabled : AllowEtwLoggingValues.Disabled;
            }

            foreach (XmlNode log in configuration.SelectNodes(LogTagXpath))
            {
                string     name = GetLogNameFromNode(log);
                LoggerType type = GetLogTypeFromNode(log);

                if (type == LoggerType.None)
                {
                    // GetLogTypeFromNode logs this particular error.
                    clean = false;
                    continue;
                }

                if (type == LoggerType.Console)
                {
                    if (name != null)
                    {
                        InternalLogger.Write.InvalidConfiguration("console log should not have a name");
                        clean = false;
                    }

                    // We use a special name for the console logger that is invalid for file loggers so we can track
                    // it along with them.
                    name = ConsoleLoggerName;
                }
                else
                {
                    if (string.IsNullOrEmpty(name))
                    {
                        InternalLogger.Write.InvalidConfiguration("cannot configure a log with no name");
                        clean = false;
                        continue;
                    }
                    if (name.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
                    {
                        InternalLogger.Write.InvalidConfiguration("base name of log is invalid " + name);
                        clean = false;
                        continue;
                    }

                    if (type == LoggerType.ETLFile && AllowEtwLogging == AllowEtwLoggingValues.Disabled)
                    {
                        InternalLogger.Write.OverridingEtwLogging(name);
                        type = LoggerType.TextLogFile;
                    }
                }

                // We wish to update existing configuration where possible.
                LogConfiguration config;
                if (!loggers.TryGetValue(name, out config))
                {
                    config = new LogConfiguration();
                }

                config.FileType = type;
                clean          &= ParseLogNode(log, config);

                if (config.NamedSources.Count + config.GuidSources.Count == 0)
                {
                    InternalLogger.Write.InvalidConfiguration("log destination " + name + " has no valid sources");
                    clean = false;
                    continue;
                }

                // Ensure what we got has been sanitized. We currently don't do stringent checks on the console logger
                // to see if useless stuff like a rotation interval or buffer size is set, but could in the future get
                // more picky.
                if (config.Filters.Count > 0 && !config.HasFeature(LogConfiguration.Features.RegexFilter))
                {
                    InternalLogger.Write.InvalidConfiguration("log destination " + name + " has filters but type " +
                                                              type + " does not support this feature.");
                    clean = false;
                    config.Filters.Clear();
                }

                loggers[name] = config;
            }

            return(clean);
        }
        private static bool ParseXmlConfiguration(XmlDocument xDocument, out Configuration configuration)
        {
            var clean           = true;
            var allowEtwLogging = AllowEtwLoggingValues.None;
            var logs            = new HashSet <LogConfiguration>();

            XmlNode node = xDocument.SelectSingleNode(EtwOverrideXpath);

            if (node != null)
            {
                XmlNode setting = node.Attributes.GetNamedItem(EtwOverrideEnabledAttribute);
                bool    isEnabled;
                if (setting == null || !bool.TryParse(setting.Value, out isEnabled))
                {
                    InternalLogger.Write.InvalidConfiguration(EtwOverrideXpath + " tag has invalid " +
                                                              EtwOverrideEnabledAttribute + " attribute");
                    clean = false;
                }
                else
                {
                    allowEtwLogging = isEnabled ? AllowEtwLoggingValues.Enabled : AllowEtwLoggingValues.Disabled;
                }
            }

            foreach (XmlNode log in xDocument.SelectNodes(LogTagXpath))
            {
                string  name = null;
                LogType type;
                if (log.Attributes[LogNameAttribute] != null)
                {
                    name = log.Attributes[LogNameAttribute].Value.Trim();
                }

                // If no type is provided we currently default to text.
                if (log.Attributes[LogTypeAttribute] == null)
                {
                    type = LogType.Text;
                }
                else
                {
                    type = log.Attributes[LogTypeAttribute].Value.ToLogType();
                }

                if (type == LogType.None)
                {
                    InternalLogger.Write.InvalidConfiguration("invalid log type " +
                                                              log.Attributes[LogTypeAttribute].Value);
                    clean = false;
                    continue;
                }

                if (type == LogType.Console)
                {
                    if (name != null)
                    {
                        InternalLogger.Write.InvalidConfiguration("console log should not have a name");
                        clean = false;
                    }
                }

                // If a log is listed in duplicates we will discard the previous data entirely. This is a change from historic
                // (pre OSS-release) behavior which was... quasi-intentional shall we say. The author is unaware of anybody
                // using this capability and, since it confusing at best, would like for it to go away.
                try
                {
                    List <EventProviderSubscription> subscriptions;
                    List <string> filters;

                    clean &= ParseLogSources(log, out subscriptions);
                    ParseLogFilters(log, out filters);
                    var config = new LogConfiguration(name, type, subscriptions, filters);

                    clean &= ParseLogNode(log, config);

                    config.Validate();
                    if (!logs.Add(config))
                    {
                        InternalLogger.Write.InvalidConfiguration($"duplicate log {log.Name} discarded.");
                        clean = false;
                    }
                }
                catch (InvalidConfigurationException e)
                {
                    InternalLogger.Write.InvalidConfiguration(e.Message);
                    clean = false;
                }
            }

            configuration = logs.Count > 0 ? new Configuration(logs, allowEtwLogging) : null;
            return(clean);
        }