static SerilogLoggerProvider CreateProvider(
            string serverUrl,
            string apiKey,
            LogLevel minimumLevel,
            IDictionary <string, LogLevel> levelOverrides)
        {
            var levelSwitch = new LoggingLevelSwitch(minimumLevel);

            var sink = new SeqSink(
                serverUrl,
                apiKey,
                1000,
                TimeSpan.FromSeconds(2),
                256 * 1024,
                levelSwitch,
                null);

            LevelOverrideMap overrideMap = null;

            if (levelOverrides != null && levelOverrides.Count != 0)
            {
                var overrides = new Dictionary <string, LoggingLevelSwitch>();
                foreach (var levelOverride in levelOverrides)
                {
                    overrides.Add(levelOverride.Key, new LoggingLevelSwitch(levelOverride.Value));
                }

                overrideMap = new LevelOverrideMap(overrides, levelSwitch);
            }

            var logger   = new Logger(levelSwitch, sink, sink.Dispose, overrideMap);
            var provider = new SerilogLoggerProvider(logger);

            return(provider);
        }
        /// <summary>
        /// Adds a sink that writes log events to a http://getseq.net Seq event server.
        /// </summary>
        /// <param name="loggerSinkConfiguration">The logger configuration.</param>
        /// <param name="serverUrl">The base URL of the Seq server that log events will be written to.</param>
        /// <param name="restrictedToMinimumLevel">The minimum log event level required
        /// in order to write an event to the sink.</param>
        /// <param name="batchPostingLimit">The maximum number of events to post in a single batch.</param>
        /// <param name="period">The time to wait between checking for event batches.</param>
        /// <param name="bufferBaseFilename">Path for a set of files that will be used to buffer events until they
        /// can be successfully transmitted across the network. Individual files will be created using the
        /// pattern <paramref name="bufferBaseFilename"/>-{Date}.json.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <returns>Logger configuration, allowing configuration to continue.</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration Seq(
            this LoggerSinkConfiguration loggerSinkConfiguration,
            string serverUrl,
            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
            int batchPostingLimit     = SeqSink.DefaultBatchPostingLimit,
            TimeSpan?period           = null,
            string apiKey             = null,
            string bufferBaseFilename = null)
        {
            if (loggerSinkConfiguration == null)
            {
                throw new ArgumentNullException("loggerSinkConfiguration");
            }
            if (serverUrl == null)
            {
                throw new ArgumentNullException("serverUrl");
            }

            var defaultedPeriod = period ?? SeqSink.DefaultPeriod;

            ILogEventSink sink;

            if (bufferBaseFilename == null)
            {
                sink = new SeqSink(serverUrl, apiKey, batchPostingLimit, defaultedPeriod);
            }
            else
            {
                sink = new DurableSeqSink(serverUrl, bufferBaseFilename, apiKey, batchPostingLimit, defaultedPeriod);
            }

            return(loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel));
        }
Example #3
0
        public void EventsAreFormattedIntoJsonPayloads()
        {
            var evt  = Some.LogEvent("Hello, {Name}!", "Alice");
            var json = SeqSink.FormatRawPayload(new[] { evt }, null);

            Assert.Contains("Name\":\"Alice", json);
        }
Example #4
0
        public void EventsAreDroppedWhenCompactJsonRenderingFails()
        {
            var evt  = Some.LogEvent(new NastyException(), "Hello, {Name}!", "Alice");
            var json = SeqSink.FormatCompactPayload(new[] { evt }, null);

            Assert.Empty(json);
        }
Example #5
0
        public void EventsAreDroppedWhenJsonRenderingFails()
        {
            var evt  = Some.LogEvent(new NastyException(), "Hello, {Name}!", "Alice");
            var json = SeqSink.FormatRawPayload(new[] { evt }, null);

            Assert.Contains("[]", json);
        }
        /// <summary>
        /// Subscribes to an <see cref="IObservable{EventEntry}" /> using a <see cref="SeqSink" />.
        /// </summary>
        /// <param name="eventStream">The event stream. Typically this is an instance of <see cref="ObservableEventListener" />.</param>
        /// <param name="serverUrl">The base URL of the Seq server that log events will be written to.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <param name="bufferingInterval">The buffering interval between each batch publishing. Default value is <see cref="Buffering.DefaultBufferingInterval" />.</param>
        /// <param name="onCompletedTimeout">Time limit for flushing the entries after an <see cref="SeqSink.OnCompleted" /> call is received.</param>
        /// <param name="bufferingCount">Number of entries that will trigger batch publishing. Default is <see cref="Buffering.DefaultBufferingCount" /></param>
        /// <param name="maxBufferSize">The maximum number of entries that can be buffered before the sink starts dropping entries.
        /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Normally, calling <see cref="IDisposable.Dispose" /> on
        /// the <see cref="System.Diagnostics.Tracing.EventListener" /> will block until all the entries are flushed or the interval elapses.
        /// If <see langword="null" /> is specified, then the call will block indefinitely until the flush operation finishes.</param>
        /// <returns>
        /// A subscription to the sink that can be disposed to unsubscribe the sink and dispose it, or to get access to the sink instance.
        /// </returns>
        public static SinkSubscription<SeqSink> LogToSeq(
            this IObservable<EventEntry> eventStream,
            string serverUrl,
            string apiKey = null,
            TimeSpan? bufferingInterval = null,
            TimeSpan? onCompletedTimeout = null,
            int bufferingCount = Buffering.DefaultBufferingCount,
            int maxBufferSize = Buffering.DefaultMaxBufferSize)
        {
            var sink = new SeqSink(
                serverUrl,
                apiKey,
                bufferingInterval ?? Buffering.DefaultBufferingInterval,
                bufferingCount,
                maxBufferSize,
                onCompletedTimeout ?? Timeout.InfiniteTimeSpan);

            var subscription = eventStream.Subscribe(sink);
            return new SinkSubscription<SeqSink>(subscription, sink);
        }
Example #7
0
        /// <summary>
        /// Subscribes to an <see cref="IObservable{EventEntry}" /> using a <see cref="SeqSink" />.
        /// </summary>
        /// <param name="eventStream">The event stream. Typically this is an instance of <see cref="ObservableEventListener" />.</param>
        /// <param name="serverUrl">The base URL of the Seq server that log events will be written to.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <param name="bufferingInterval">The buffering interval between each batch publishing. Default value is <see cref="Buffering.DefaultBufferingInterval" />.</param>
        /// <param name="onCompletedTimeout">Time limit for flushing the entries after an <see cref="SeqSink.OnCompleted" /> call is received.</param>
        /// <param name="bufferingCount">Number of entries that will trigger batch publishing. Default is <see cref="Buffering.DefaultBufferingCount" /></param>
        /// <param name="maxBufferSize">The maximum number of entries that can be buffered before the sink starts dropping entries.
        /// This means that if the timeout period elapses, some event entries will be dropped and not sent to the store. Normally, calling <see cref="IDisposable.Dispose" /> on
        /// the <see cref="System.Diagnostics.Tracing.EventListener" /> will block until all the entries are flushed or the interval elapses.
        /// If <see langword="null" /> is specified, then the call will block indefinitely until the flush operation finishes.</param>
        /// <returns>
        /// A subscription to the sink that can be disposed to unsubscribe the sink and dispose it, or to get access to the sink instance.
        /// </returns>
        public static SinkSubscription <SeqSink> LogToSeq(
            this IObservable <EventEntry> eventStream,
            string serverUrl,
            string apiKey = null,
            TimeSpan?bufferingInterval  = null,
            TimeSpan?onCompletedTimeout = null,
            int bufferingCount          = Buffering.DefaultBufferingCount,
            int maxBufferSize           = Buffering.DefaultMaxBufferSize)
        {
            var sink = new SeqSink(
                serverUrl,
                apiKey,
                bufferingInterval ?? Buffering.DefaultBufferingInterval,
                bufferingCount,
                maxBufferSize,
                onCompletedTimeout ?? Timeout.InfiniteTimeSpan);

            var subscription = eventStream.Subscribe(sink);

            return(new SinkSubscription <SeqSink>(subscription, sink));
        }
Example #8
0
        /// <summary>
        /// Adds a sink that writes log events to a http://getseq.net Seq event server.
        /// </summary>
        /// <param name="loggerSinkConfiguration">The logger configuration.</param>
        /// <param name="serverUrl">The base URL of the Seq server that log events will be written to.</param>
        /// <param name="restrictedToMinimumLevel">The minimum log event level required
        /// in order to write an event to the sink.</param>
        /// <param name="batchPostingLimit">The maximum number of events to post in a single batch.</param>
        /// <param name="period">The time to wait between checking for event batches.</param>
        /// <param name="bufferBaseFilename">Path for a set of files that will be used to buffer events until they
        /// can be successfully transmitted across the network. Individual files will be created using the
        /// pattern <paramref name="bufferBaseFilename"/>-{Date}.json.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <param name="bufferFileSizeLimitBytes">The maximum size, in bytes, to which the buffer
        /// log file for a specific date will be allowed to grow. By default no limit will be applied.</param>
        /// <returns>Logger configuration, allowing configuration to continue.</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration Seq(
            this LoggerSinkConfiguration loggerSinkConfiguration,
            string serverUrl,
            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
            int batchPostingLimit         = SeqSink.DefaultBatchPostingLimit,
            TimeSpan?period               = null,
            string apiKey                 = null,
            string bufferBaseFilename     = null,
            long?bufferFileSizeLimitBytes = null)
        {
            if (loggerSinkConfiguration == null)
            {
                throw new ArgumentNullException("loggerSinkConfiguration");
            }
            if (serverUrl == null)
            {
                throw new ArgumentNullException("serverUrl");
            }
            if (bufferFileSizeLimitBytes.HasValue && bufferFileSizeLimitBytes < 0)
            {
                throw new ArgumentException("Negative value provided; file size limit must be non-negative");
            }

            var defaultedPeriod = period ?? SeqSink.DefaultPeriod;

            ILogEventSink sink;

            if (bufferBaseFilename == null)
            {
                sink = new SeqSink(serverUrl, apiKey, batchPostingLimit, defaultedPeriod);
            }
            else
            {
                sink = new DurableSeqSink(serverUrl, bufferBaseFilename, apiKey, batchPostingLimit, defaultedPeriod, bufferFileSizeLimitBytes);
            }

            return(loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel));
        }
        /// <summary>
        /// Adds a Seq logger configured from the supplied configuration section.
        /// </summary>
        /// <param name="loggerFactory">The logger factory.</param>
        /// <param name="serverUrl"></param>
        /// <param name="apiKey">A Seq API key to authenticate or tag messages from the logger.</param>
        /// <param name="minimumLevel">The level below which events will be suppressed (the default is <see cref="LogLevel.Information"/>).</param>
        /// <param name="levelOverrides">A dictionary mapping logger name prefixes to minimum logging levels.</param>
        /// <returns>A logger factory to allow further configuration.</returns>
        public static ILoggerFactory AddSeq(
            this ILoggerFactory loggerFactory,
            string serverUrl,
            string apiKey         = null,
            LogLevel minimumLevel = LogLevel.Information,
            IDictionary <string, LogLevel> levelOverrides = null)
        {
            var levelSwitch = new LoggingLevelSwitch(minimumLevel);

            var sink = new SeqSink(
                serverUrl,
                apiKey,
                1000,
                TimeSpan.FromSeconds(2),
                256 * 1024,
                levelSwitch,
                null);

            LevelOverrideMap overrideMap = null;

            if (levelOverrides != null && levelOverrides.Count != 0)
            {
                var overrides = new Dictionary <string, LoggingLevelSwitch>();
                foreach (var levelOverride in levelOverrides)
                {
                    overrides.Add(levelOverride.Key, new LoggingLevelSwitch(levelOverride.Value));
                }

                overrideMap = new LevelOverrideMap(overrides, levelSwitch);
            }

            var logger = new Logger(levelSwitch, sink, new LogContextEnricher(), sink.Dispose, overrideMap);

            loggerFactory.AddProvider(new SerilogLoggerProvider(logger));

            return(loggerFactory);
        }
Example #10
0
        /// <summary>
        /// Adds a sink that writes log events to a <a href="https://getseq.net">Seq</a> server.
        /// </summary>
        /// <param name="loggerSinkConfiguration">The logger configuration.</param>
        /// <param name="serverUrl">The base URL of the Seq server that log events will be written to.</param>
        /// <param name="restrictedToMinimumLevel">The minimum log event level required
        /// in order to write an event to the sink.</param>
        /// <param name="batchPostingLimit">The maximum number of events to post in a single batch.</param>
        /// <param name="period">The time to wait between checking for event batches.</param>
        /// <param name="bufferBaseFilename">Path for a set of files that will be used to buffer events until they
        /// can be successfully transmitted across the network. Individual files will be created using the
        /// pattern <paramref name="bufferBaseFilename"/>-{Date}.json.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <param name="bufferFileSizeLimitBytes">The maximum size, in bytes, to which the buffer
        /// log file for a specific date will be allowed to grow. By default no limit will be applied.</param>
        /// <param name="eventBodyLimitBytes">The maximum size, in bytes, that the JSON representation of
        /// an event may take before it is dropped rather than being sent to the Seq server. Specify null for no limit.
        /// The default is 265 KB.</param>
        /// <param name="controlLevelSwitch">If provided, the switch will be updated based on the Seq server's level setting
        /// for the corresponding API key. Passing the same key to MinimumLevel.ControlledBy() will make the whole pipeline
        /// dynamically controlled. Do not specify <paramref name="restrictedToMinimumLevel"/> with this setting.</param>
        /// <param name="messageHandler">Used to construct the HttpClient that will send the log messages to Seq.</param>
        /// <param name="retainedInvalidPayloadsLimitBytes">A soft limit for the number of bytes to use for storing failed requests.
        /// The limit is soft in that it can be exceeded by any single error payload, but in that case only that single error
        /// payload will be retained.</param>
        /// <param name="compact">Use the compact log event format defined by
        /// <a href="https://github.com/serilog/serilog-formatting-compact">Serilog.Formatting.Compact</a>. Has no effect on
        /// durable log shipping. Requires Seq 3.3+.</param>
        /// <param name="queueSizeLimit">The maximum number of events that will be held in-memory while waiting to ship them to
        /// Seq. Beyond this limit, events will be dropped. The default is 100,000. Has no effect on
        /// durable log shipping.</param>
        /// <returns>Logger configuration, allowing configuration to continue.</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration Seq(
            this LoggerSinkConfiguration loggerSinkConfiguration,
            string serverUrl,
            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
            int batchPostingLimit                  = SeqSink.DefaultBatchPostingLimit,
            TimeSpan?period                        = null,
            string apiKey                          = null,
            string bufferBaseFilename              = null,
            long?bufferFileSizeLimitBytes          = null,
            long?eventBodyLimitBytes               = 256 *1024,
            LoggingLevelSwitch controlLevelSwitch  = null,
            HttpMessageHandler messageHandler      = null,
            long?retainedInvalidPayloadsLimitBytes = null,
            bool compact       = false,
            int queueSizeLimit = SeqSink.DefaultQueueSizeLimit)
        {
            if (loggerSinkConfiguration == null)
            {
                throw new ArgumentNullException(nameof(loggerSinkConfiguration));
            }
            if (serverUrl == null)
            {
                throw new ArgumentNullException(nameof(serverUrl));
            }
            if (bufferFileSizeLimitBytes.HasValue && bufferFileSizeLimitBytes < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(bufferFileSizeLimitBytes), "Negative value provided; file size limit must be non-negative.");
            }
            if (queueSizeLimit < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(queueSizeLimit), "Queue size limit must be non-zero.");
            }

            var defaultedPeriod = period ?? SeqSink.DefaultPeriod;

            ILogEventSink sink;

            if (bufferBaseFilename == null)
            {
                sink = new SeqSink(
                    serverUrl,
                    apiKey,
                    batchPostingLimit,
                    defaultedPeriod,
                    eventBodyLimitBytes,
                    controlLevelSwitch,
                    messageHandler,
                    compact,
                    queueSizeLimit);
            }
            else
            {
#if DURABLE
                sink = new DurableSeqSink(
                    serverUrl,
                    bufferBaseFilename,
                    apiKey,
                    batchPostingLimit,
                    defaultedPeriod,
                    bufferFileSizeLimitBytes,
                    eventBodyLimitBytes,
                    controlLevelSwitch,
                    messageHandler,
                    retainedInvalidPayloadsLimitBytes);
#else
                // We keep the API consistent for easier packaging and to support bait-and-switch.
                throw new NotSupportedException("Durable log shipping is not supported on this platform.");
#endif
            }

            return(loggerSinkConfiguration.Sink(sink, restrictedToMinimumLevel));
        }