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(); }
public void WithNoSwitchToControlAllEventsAreIncludedAfterReset() { var cls = new ControlledLevelSwitch(null); cls.Update(LogEventLevel.Warning); cls.Update(null); Assert.True(cls.IsIncluded(Some.DebugEvent())); }
public void WithNoSwitchToControlEventsAreStillFiltered() { var cls = new ControlledLevelSwitch(null); cls.Update(LogEventLevel.Warning); Assert.True(cls.IsIncluded(Some.ErrorEvent())); Assert.False(cls.IsIncluded(Some.InformationEvent())); }
public void WhenTheServerSendsALevelTheSwitchIsAdjusted() { var lls = new LoggingLevelSwitch(LogEventLevel.Warning); var cls = new ControlledLevelSwitch(lls); cls.Update(LogEventLevel.Debug); Assert.Equal(LogEventLevel.Debug, lls.MinimumLevel); }
public void WhenTheServerSendsNoLevelTheSwitchIsNotInitiallyAdjusted() { var lls = new LoggingLevelSwitch(LogEventLevel.Fatal); var cls = new ControlledLevelSwitch(lls); cls.Update(null); Assert.Equal(LogEventLevel.Fatal, lls.MinimumLevel); }
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); }
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)); }
public void AfterServerControlhTheControllerIsAlwaysActive() { var cls = new ControlledLevelSwitch(); cls.Update(LogEventLevel.Information); Assert.True(cls.IsActive); cls.Update(null); Assert.True(cls.IsActive); }
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(); }
public void WhenNotControllingASwitchTheControllerIsNotActive() { var cls = new ControlledLevelSwitch(); Assert.False(cls.IsActive); }
public void WhenControllingASwitchTheControllerIsActive() { var cls = new ControlledLevelSwitch(new LoggingLevelSwitch()); Assert.True(cls.IsActive); }
public void WithNoSwitchToControlAllEventsAreIncluded() { var cls = new ControlledLevelSwitch(null); Assert.True(cls.IsIncluded(Some.DebugEvent())); }
/// <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))); }