コード例 #1
0
        public HttpLogShipper(
            string serverUrl,
            string bufferBaseFilename,
            string apiKey,
            int batchPostingLimit,
            TimeSpan period,
            long?eventBodyLimitBytes,
            LoggingLevelSwitch levelControlSwitch,
            HttpMessageHandler messageHandler,
            long?retainedInvalidPayloadsLimitBytes,
            long?bufferSizeLimitBytes)
        {
            _apiKey              = apiKey;
            _batchPostingLimit   = batchPostingLimit;
            _eventBodyLimitBytes = eventBodyLimitBytes;
            _controlledSwitch    = new ControlledLevelSwitch(levelControlSwitch);
            _connectionSchedule  = new ExponentialBackoffConnectionSchedule(period);
            _retainedInvalidPayloadsLimitBytes = retainedInvalidPayloadsLimitBytes;
            _bufferSizeLimitBytes   = bufferSizeLimitBytes;
            _httpClient             = messageHandler != null ? new HttpClient(messageHandler) : new HttpClient();
            _httpClient.BaseAddress = new Uri(SeqApi.NormalizeServerBaseAddress(serverUrl));
            _fileSet = new FileSet(bufferBaseFilename);
            _timer   = new PortableTimer(c => OnTick());

            SetTimer();
        }
コード例 #2
0
        public void WithNoSwitchToControlAllEventsAreIncludedAfterReset()
        {
            var cls = new ControlledLevelSwitch(null);

            cls.Update(LogEventLevel.Warning);
            cls.Update(null);
            Assert.True(cls.IsIncluded(Some.DebugEvent()));
        }
コード例 #3
0
        public void WithNoSwitchToControlEventsAreStillFiltered()
        {
            var cls = new ControlledLevelSwitch(null);

            cls.Update(LogEventLevel.Warning);
            Assert.True(cls.IsIncluded(Some.ErrorEvent()));
            Assert.False(cls.IsIncluded(Some.InformationEvent()));
        }
コード例 #4
0
        public void WhenTheServerSendsALevelTheSwitchIsAdjusted()
        {
            var lls = new LoggingLevelSwitch(LogEventLevel.Warning);
            var cls = new ControlledLevelSwitch(lls);

            cls.Update(LogEventLevel.Debug);
            Assert.Equal(LogEventLevel.Debug, lls.MinimumLevel);
        }
コード例 #5
0
        public void WhenTheServerSendsNoLevelTheSwitchIsNotInitiallyAdjusted()
        {
            var lls = new LoggingLevelSwitch(LogEventLevel.Fatal);
            var cls = new ControlledLevelSwitch(lls);

            cls.Update(null);
            Assert.Equal(LogEventLevel.Fatal, lls.MinimumLevel);
        }
コード例 #6
0
        public void WhenTheServerSendsNoLevelTheSwitchIsResetIfPreviouslyAdjusted()
        {
            var lls = new LoggingLevelSwitch(LogEventLevel.Warning);
            var cls = new ControlledLevelSwitch(lls);

            cls.Update(LogEventLevel.Information);
            cls.Update(null);
            Assert.Equal(LogEventLevel.Warning, lls.MinimumLevel);
        }
コード例 #7
0
 public BatchedSeqSink(
     SeqIngestionApi ingestionApi,
     long?eventBodyLimitBytes,
     ControlledLevelSwitch controlledSwitch)
 {
     _controlledSwitch = controlledSwitch ?? throw new ArgumentNullException(nameof(controlledSwitch));
     _formatter        = new ConstrainedBufferedFormatter(eventBodyLimitBytes);
     _ingestionApi     = ingestionApi ?? throw new ArgumentNullException(nameof(ingestionApi));
 }
コード例 #8
0
        public void AfterServerControlhTheControllerIsAlwaysActive()
        {
            var cls = new ControlledLevelSwitch();

            cls.Update(LogEventLevel.Information);
            Assert.True(cls.IsActive);

            cls.Update(null);
            Assert.True(cls.IsActive);
        }
コード例 #9
0
        public DurableSeqSink(
            string serverUrl,
            string bufferBaseFilename,
            string?apiKey,
            int batchPostingLimit,
            TimeSpan period,
            long?bufferSizeLimitBytes,
            long?eventBodyLimitBytes,
            ControlledLevelSwitch controlledSwitch,
            HttpMessageHandler?messageHandler,
            long?retainedInvalidPayloadsLimitBytes)
        {
            if (serverUrl == null)
            {
                throw new ArgumentNullException(nameof(serverUrl));
            }
            if (bufferBaseFilename == null)
            {
                throw new ArgumentNullException(nameof(bufferBaseFilename));
            }

            var fileSet = new FileSet(bufferBaseFilename);

            _shipper = new HttpLogShipper(
                fileSet,
                new SeqIngestionApiClient(serverUrl, apiKey, messageHandler),
                batchPostingLimit,
                period,
                eventBodyLimitBytes,
                controlledSwitch,
                retainedInvalidPayloadsLimitBytes,
                bufferSizeLimitBytes);

            const long individualFileSizeLimitBytes = 100L * 1024 * 1024;

            _sink = new LoggerConfiguration()
                    .MinimumLevel.Verbose()
                    .WriteTo.File(new ConstrainedBufferedFormatter(eventBodyLimitBytes),
                                  fileSet.RollingFilePathFormat,
                                  rollingInterval: RollingInterval.Day,
                                  fileSizeLimitBytes: individualFileSizeLimitBytes,
                                  rollOnFileSizeLimit: true,
                                  retainedFileCountLimit: null,
                                  encoding: Encoding.UTF8)
                    .CreateLogger();
        }
コード例 #10
0
        public void WhenNotControllingASwitchTheControllerIsNotActive()
        {
            var cls = new ControlledLevelSwitch();

            Assert.False(cls.IsActive);
        }
コード例 #11
0
        public void WhenControllingASwitchTheControllerIsActive()
        {
            var cls = new ControlledLevelSwitch(new LoggingLevelSwitch());

            Assert.True(cls.IsActive);
        }
コード例 #12
0
        public void WithNoSwitchToControlAllEventsAreIncluded()
        {
            var cls = new ControlledLevelSwitch(null);

            Assert.True(cls.IsIncluded(Some.DebugEvent()));
        }
コード例 #13
0
        /// <summary>
        /// Write log events to a <a href="https://datalust.co/seq">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"/>*.json, which should not clash with any other filenames
        /// in the same directory.</param>
        /// <param name="apiKey">A Seq <i>API key</i> that authenticates the client to the Seq server.</param>
        /// <param name="bufferSizeLimitBytes">The maximum amount of data, 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="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                  = DefaultBatchPostingLimit,
            TimeSpan?period                        = null,
            string?apiKey                          = null,
            string?bufferBaseFilename              = null,
            long?bufferSizeLimitBytes              = null,
            long?eventBodyLimitBytes               = 256 *1024,
            LoggingLevelSwitch?controlLevelSwitch  = null,
            HttpMessageHandler?messageHandler      = null,
            long?retainedInvalidPayloadsLimitBytes = null,
            int queueSizeLimit                     = DefaultQueueSizeLimit)
        {
            if (loggerSinkConfiguration == null)
            {
                throw new ArgumentNullException(nameof(loggerSinkConfiguration));
            }
            if (serverUrl == null)
            {
                throw new ArgumentNullException(nameof(serverUrl));
            }
            if (bufferSizeLimitBytes is < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(bufferSizeLimitBytes), "Negative value provided; buffer size limit must be non-negative.");
            }
            if (queueSizeLimit < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(queueSizeLimit), "Queue size limit must be non-zero.");
            }

            var defaultedPeriod  = period ?? DefaultPeriod;
            var controlledSwitch = new ControlledLevelSwitch(controlLevelSwitch);

            ILogEventSink sink;

            if (bufferBaseFilename == null)
            {
                var batchedSink = new BatchedSeqSink(
                    new SeqIngestionApiClient(serverUrl, apiKey, messageHandler),
                    eventBodyLimitBytes,
                    controlledSwitch);

                var options = new PeriodicBatchingSinkOptions
                {
                    BatchSizeLimit = batchPostingLimit,
                    Period         = defaultedPeriod,
                    QueueLimit     = queueSizeLimit
                };

                sink = new PeriodicBatchingSink(batchedSink, options);
            }
            else
            {
#if DURABLE
                sink = new DurableSeqSink(
                    serverUrl,
                    bufferBaseFilename,
                    apiKey,
                    batchPostingLimit,
                    defaultedPeriod,
                    bufferSizeLimitBytes,
                    eventBodyLimitBytes,
                    controlledSwitch,
                    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.Conditional(
                       controlledSwitch.IsIncluded,
                       wt => wt.Sink(sink, restrictedToMinimumLevel)));
        }