/// <summary> /// <see cref="LoggerSinkConfiguration"/> extension that provides configuration chaining. /// </summary> /// <param name="loggerSinkConfiguration">Instance of <see cref="LoggerSinkConfiguration"/> object.</param> /// <param name="telegramSinkOptions">The Telegram sink options object.</param> /// <param name="restrictedToMinimumLevel"><see cref="LogEventLevel"/> value that specifies minimum logging /// level that will be allowed to be logged.</param> /// <returns>Instance of <see cref="LoggerConfiguration"/> object.</returns> public static LoggerConfiguration Telegram( this LoggerSinkConfiguration loggerSinkConfiguration, TelegramSinkOptions telegramSinkOptions, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) { if (loggerSinkConfiguration is null) { throw new ArgumentNullException(nameof(loggerSinkConfiguration), "The logger sink configuration is null."); } if (telegramSinkOptions is null) { throw new ArgumentNullException(nameof(telegramSinkOptions), "The Telegram sink options are null."); } if (string.IsNullOrWhiteSpace(telegramSinkOptions.BotToken)) { throw new ArgumentNullException(nameof(telegramSinkOptions.BotToken), "The Telegram bot token is null."); } var batchingOptions = new PeriodicBatchingSinkOptions() { BatchSizeLimit = telegramSinkOptions.BatchSizeLimit, Period = telegramSinkOptions.Period }; var batchingSink = new PeriodicBatchingSink(new TelegramSink(telegramSinkOptions), batchingOptions); return(loggerSinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel)); }
/// <summary> /// Initializes a new instance of the <see cref="TelegramSink"/> class. /// </summary> /// <param name="options">Telegram options object.</param> public TelegramSink(TelegramSinkOptions options) { this.options = options; if (!string.IsNullOrWhiteSpace(options.OutputTemplate)) { this.outputTemplateRenderer = new OutputTemplateRenderer(options.OutputTemplate!, options); } }
/// <summary> /// Initializes a new instance of the <see cref="OutputTemplateRenderer"/> class. /// </summary> /// <param name="outputTemplate">The output template.</param> /// <param name="options">The options.</param> internal OutputTemplateRenderer(string outputTemplate, TelegramSinkOptions options) { MessageTemplate template = new MessageTemplateParser().Parse(outputTemplate); var renderActions = new List <Action <ExtendedLogEvent, TextWriter> >(); foreach (MessageTemplateToken token in template.Tokens) { switch (token) { case TextToken textToken: renderActions.Add((_, w) => TextTokenRenderer.Render(textToken, w, options)); break; case PropertyToken propertyToken: switch (propertyToken.PropertyName) { case OutputProperties.LevelPropertyName: renderActions.Add(new LogLevelRenderer(propertyToken).Render); break; case OutputProperties.NewLinePropertyName: renderActions.Add((_, w) => w.WriteLine()); break; case OutputProperties.ExceptionPropertyName: renderActions.Add(new ExceptionRenderer(options).Render); break; case OutputProperties.MessagePropertyName: renderActions.Add(new MessageRenderer(options).Render); break; case OutputProperties.TimestampPropertyName: renderActions.Add(new TimestampRenderer(propertyToken).Render); break; default: renderActions.Add(new DefaultPropertyRenderer(propertyToken).Render); break; } break; } } this.renderActions = renderActions.ToArray(); }
/// <summary> /// Renders the message. /// </summary> /// <param name="extLogEvent">The log event.</param> /// <param name="options">The options.</param> /// <returns>The rendered message.</returns> private static string RenderMessage(ExtendedLogEvent extLogEvent, TelegramSinkOptions options) { var sb = new StringBuilder(); var emoji = LogLevelRenderer.GetEmoji(extLogEvent.LogEvent); var renderedMessage = HtmlEscaper.Escape(options, extLogEvent.LogEvent.RenderMessage()); sb.AppendLine($"{emoji} {renderedMessage}"); sb.AppendLine(string.Empty); if (!string.IsNullOrWhiteSpace(options.ApplicationName) || !string.IsNullOrWhiteSpace(options.DateFormat)) { string applicationNamePart = string.IsNullOrWhiteSpace(options.ApplicationName) ? string.Empty : $"{HtmlEscaper.Escape(options, options.ApplicationName)}: "; string datePart = string.IsNullOrWhiteSpace(options.DateFormat) ? string.Empty : extLogEvent.FirstOccurrence != extLogEvent.LastOccurrence ? $"The message occurred first on {extLogEvent.FirstOccurrence.ToString(options.DateFormat)} and last on {extLogEvent.LastOccurrence.ToString(options.DateFormat)}" : $"The message occurred on {extLogEvent.FirstOccurrence.ToString(options.DateFormat)}"; sb.AppendLine($"<i>{applicationNamePart}{datePart}</i>"); } if (extLogEvent.LogEvent.Exception is null) { return sb.ToString(); } var message = HtmlEscaper.Escape(options, extLogEvent.LogEvent.Exception.Message); var exceptionType = HtmlEscaper.Escape(options, extLogEvent.LogEvent.Exception.GetType().Name); sb.AppendLine($"\n<strong>{message}</strong>\n"); sb.AppendLine($"Message: <code>{message}</code>"); sb.AppendLine($"Type: <code>{exceptionType}</code>\n"); if (extLogEvent.IncludeStackTrace) { var exception = HtmlEscaper.Escape(options, $"{extLogEvent.LogEvent.Exception}"); sb.AppendLine($"Stack Trace\n<code>{exception}</code>"); } return sb.ToString(); }
/// <summary> /// <see cref="LoggerSinkConfiguration"/> extension that provides configuration chaining. /// <example> /// new LoggerConfiguration() /// .MinimumLevel.Verbose() /// .WriteTo.Telegram("botToken", "chatId") /// .CreateLogger(); /// </example> /// </summary> /// <param name="loggerSinkConfiguration">Instance of <see cref="LoggerSinkConfiguration"/> object.</param> /// <param name="botToken">The Telegram bot token.</param> /// <param name="chatId">The Telegram chat id.</param> /// <param name="batchSizeLimit">The maximum number of events to post in a single batch; defaults to 1 if /// not provided i.e. no batching by default.</param> /// <param name="period">The time to wait between checking for event batches; defaults to 1 sec if not /// provided.</param> /// <param name="formatProvider">The format provider used for formatting the message.</param> /// <param name="restrictedToMinimumLevel"><see cref="LogEventLevel"/> value that specifies minimum logging /// level that will be allowed to be logged.</param> /// <param name="visibleProperties">A value indicating log event properties that must be appended to the message.</param> /// <returns>Instance of <see cref="LoggerConfiguration"/> object.</returns> public static LoggerConfiguration Telegram( this LoggerSinkConfiguration loggerSinkConfiguration, string botToken, string chatId = null, int?batchSizeLimit = null, TimeSpan?period = null, IFormatProvider formatProvider = null, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, IEnumerable <string> visibleProperties = null) { var telegramSinkOptions = new TelegramSinkOptions( botToken, chatId, batchSizeLimit, period, formatProvider, restrictedToMinimumLevel, visibleProperties: visibleProperties); return(loggerSinkConfiguration.Telegram(telegramSinkOptions, restrictedToMinimumLevel)); }
/// <summary> /// <see cref="LoggerSinkConfiguration"/> extension that provides configuration chaining. /// </summary> /// <param name="loggerSinkConfiguration">Instance of <see cref="LoggerSinkConfiguration"/> object.</param> /// <param name="telegramSinkOptions">The Telegram sink options object.</param> /// <param name="restrictedToMinimumLevel"><see cref="LogEventLevel"/> value that specifies minimum logging /// level that will be allowed to be logged.</param> /// <returns>Instance of <see cref="LoggerConfiguration"/> object.</returns> public static LoggerConfiguration Telegram( this LoggerSinkConfiguration loggerSinkConfiguration, TelegramSinkOptions telegramSinkOptions, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) { if (loggerSinkConfiguration == null) { throw new ArgumentNullException(nameof(loggerSinkConfiguration)); } if (telegramSinkOptions == null) { throw new ArgumentNullException(nameof(telegramSinkOptions)); } if (string.IsNullOrWhiteSpace(telegramSinkOptions.BotToken)) { throw new ArgumentNullException(nameof(telegramSinkOptions.BotToken)); } return(loggerSinkConfiguration.Sink(new TelegramSink(telegramSinkOptions), restrictedToMinimumLevel)); }
/// <summary> /// <see cref="LoggerSinkConfiguration"/> extension that provides configuration chaining. /// <example> /// new LoggerConfiguration() /// .MinimumLevel.Verbose() /// .WriteTo.Telegram("botToken", "chatId") /// .CreateLogger(); /// </example> /// </summary> /// <param name="loggerSinkConfiguration">Instance of <see cref="LoggerSinkConfiguration"/> object.</param> /// <param name="botToken">The Telegram bot token.</param> /// <param name="chatId">The Telegram chat id.</param> /// <param name="batchSizeLimit">The maximum number of events to post in a single batch; defaults to 1 if /// not provided i.e. no batching by default.</param> /// <param name="period">The time to wait between checking for event batches; defaults to 1 sec if not /// provided.</param> /// <param name="formatProvider">The format provider used for formatting the message.</param> /// <param name="restrictedToMinimumLevel"><see cref="LogEventLevel"/> value that specifies minimum logging /// level that will be allowed to be logged.</param> /// <param name="sendBatchesAsSingleMessages">A value indicating whether the batches are sent as single messages or as one block of messages.</param> /// <param name="includeStackTrace">Whether stack traces should be included or not.</param> /// <param name="dateFormat">The date time format showing how the date and time should be formatted.</param> /// <param name="applicationName">The name of the application sending the events in case multiple apps write to the same channel.</param> /// <param name="failureCallback">The failure callback.</param> /// <param name="useCustomHtmlFormatting">A value indicating whether custom HTML formatting in the messages could be used. (Use this carefully and only if really needed).</param> /// <param name="botApiUrl">The Telegram bot API url, defaults to https://api.telegram.org/bot.</param> /// <param name="outputTemplate">A output template that can be used to format the output data.</param> /// <param name="customHtmlFormatter"> /// You can pass a func in addition to <see cref="TelegramSinkOptions.UseCustomHtmlFormatting"/> to set your custom function for escaping HTML strings. /// This will only be considered if <see cref="TelegramSinkOptions.UseCustomHtmlFormatting"/> is set to true. /// </param> /// <returns>Instance of <see cref="LoggerConfiguration"/> object.</returns> public static LoggerConfiguration Telegram( this LoggerSinkConfiguration loggerSinkConfiguration, string botToken, string chatId, int?batchSizeLimit = null, TimeSpan?period = null, IFormatProvider?formatProvider = null, LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, bool?sendBatchesAsSingleMessages = true, bool?includeStackTrace = true, string dateFormat = "dd.MM.yyyy HH:mm:sszzz", string applicationName = "", Action <Exception>?failureCallback = null, bool useCustomHtmlFormatting = false, string?botApiUrl = null, string?outputTemplate = null, Func <string, string>?customHtmlFormatter = null) { var telegramSinkOptions = new TelegramSinkOptions( botToken, chatId, dateFormat, applicationName, batchSizeLimit, period, formatProvider, restrictedToMinimumLevel, sendBatchesAsSingleMessages, includeStackTrace, failureCallback, useCustomHtmlFormatting, botApiUrl, outputTemplate, customHtmlFormatter: customHtmlFormatter); return(loggerSinkConfiguration.Telegram(telegramSinkOptions, restrictedToMinimumLevel)); }
/// <summary> /// Creates a new instance of the renderer. /// </summary> /// <param name="options">The Telegram sink options.</param> public MessageRenderer(TelegramSinkOptions options) { this.options = options; }
/// <summary> /// Renders the given text <paramref name="token"/>. Results are written to the <paramref name="output"/>. /// </summary> /// <param name="token">The text token.</param> /// <param name="output">The output.</param> /// <param name="options">The sink options.</param> public static void Render(TextToken token, TextWriter output, TelegramSinkOptions options) { output.Write(HtmlEscaper.Escape(options, token.Text)); }
/// <summary> /// Correctly escapes strings taking options into consideration. /// </summary> /// <param name="options">The options specified by the consumer.</param> /// <param name="message">The string to escape.</param> /// <returns>The properly escaped string.</returns> internal static string Escape(TelegramSinkOptions options, string message) { return(options.CustomHtmlFormatter is null ? message.HtmlEscape(options.ShouldEscape) : options.CustomHtmlFormatter.Invoke(message)); }
/// <summary> /// Initializes a new instance of the <see cref="DefaultPropertyRenderer"/> class. /// </summary> /// <param name="options">The Telegram sink options.</param> public ExceptionRenderer(TelegramSinkOptions options) { this.options = options; }