This repository was archived by the owner on Dec 13, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 246
Add Blob AzureWebApp provider #477
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
src/Microsoft.Extensions.Logging.AzureWebAppDiagnostics/AzureWebAppDiagnosticsSettings.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
|
||
namespace Microsoft.Extensions.Logging.AzureWebAppDiagnostics | ||
{ | ||
/// <summary> | ||
/// Settings for <see cref="AzureWebAppDiagnosticsLoggerProvider"/>. | ||
/// </summary> | ||
public class AzureWebAppDiagnosticsSettings | ||
{ | ||
/// <summary> | ||
/// Gets or sets a strictly positive value representing the maximum log size in bytes. Once the log is full, no more message will be appended. | ||
/// </summary> | ||
public int FileSizeLimit { get; set; } = 10 * 1024 * 1024; | ||
|
||
/// <summary> | ||
/// Gets or sets a strictly positive value representing the maximum retained file count. | ||
/// </summary> | ||
public int RetainedFileCountLimit { get; set; } = 2; | ||
|
||
/// <summary> | ||
/// Gets or sets a message template describing the output messages. | ||
/// </summary> | ||
public string OutputTemplate { get; set; } = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"; | ||
|
||
/// <summary> | ||
/// Gets or sets a maximum number of events to include in a single blob append batch. | ||
/// </summary> | ||
public int BlobBatchSize { get; set; } = 32; | ||
|
||
/// <summary> | ||
/// Gets or sets a time to wait between checking for blob log batches. | ||
/// </summary> | ||
public TimeSpan BlobCommitPeriod { get; set; } = TimeSpan.FromSeconds(5); | ||
|
||
/// <summary> | ||
/// Gets or sets the last section of log blob name. | ||
/// </summary> | ||
public string BlobName { get; set; } = "applicationLog.txt"; | ||
|
||
/// Gets of sets the maximum size of the background log message queue. | ||
public int BackgroundQueueSize { get; set; } | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
src/Microsoft.Extensions.Logging.AzureWebAppDiagnostics/Internal/AzureBlobLoggerProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using Serilog; | ||
using Serilog.Core; | ||
using Serilog.Formatting.Display; | ||
using Microsoft.WindowsAzure.Storage.Blob; | ||
|
||
namespace Microsoft.Extensions.Logging.AzureWebAppDiagnostics.Internal | ||
{ | ||
/// <summary> | ||
/// The implemenation of logger provider that creates instances of <see cref="Serilog.Core.Logger"/>. | ||
/// </summary> | ||
public class AzureBlobLoggerProvider | ||
{ | ||
private readonly string _outputTemplate; | ||
private readonly string _appName; | ||
private readonly string _instanceId; | ||
private readonly string _fileName; | ||
private readonly int _batchSize; | ||
private readonly int _backgroundQueueSize; | ||
private readonly TimeSpan _period; | ||
|
||
/// <summary> | ||
/// Creates a new instance of the <see cref="AzureBlobLoggerProvider"/> class. | ||
/// </summary> | ||
/// <param name="outputTemplate">A message template describing the output messages</param> | ||
/// <param name="appName">The application name to use in blob name</param> | ||
/// <param name="instanceId">The application instance id to use in blob name</param> | ||
/// <param name="fileName">The last section in log blob name</param> | ||
/// <param name="batchSize">A maximum number of events to include in a single blob append batch</param> | ||
/// <param name="backgroundQueueSize">The maximum size of the background queue</param> | ||
/// <param name="period">A time to wait between checking for blob log batches</param> | ||
public AzureBlobLoggerProvider(string outputTemplate, string appName, string instanceId, string fileName, int batchSize, int backgroundQueueSize, TimeSpan period) | ||
{ | ||
if (outputTemplate == null) | ||
{ | ||
throw new ArgumentNullException(nameof(outputTemplate)); | ||
} | ||
if (appName == null) | ||
{ | ||
throw new ArgumentNullException(nameof(appName)); | ||
} | ||
if (instanceId == null) | ||
{ | ||
throw new ArgumentNullException(nameof(instanceId)); | ||
} | ||
if (fileName == null) | ||
{ | ||
throw new ArgumentNullException(nameof(fileName)); | ||
} | ||
if (batchSize <= 0) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(batchSize), $"{nameof(batchSize)} should be a positive number."); | ||
} | ||
if (period <= TimeSpan.Zero) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(period), $"{nameof(period)} should be longer than zero."); | ||
} | ||
|
||
_outputTemplate = outputTemplate; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any checks for these parameters? You'll probably gets some null refs later if not |
||
_appName = appName; | ||
_instanceId = instanceId; | ||
_fileName = fileName; | ||
_batchSize = batchSize; | ||
_backgroundQueueSize = backgroundQueueSize; | ||
_period = period; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Logger ConfigureLogger(IWebAppLogConfigurationReader reader) | ||
{ | ||
var messageFormatter = new MessageTemplateTextFormatter(_outputTemplate, null); | ||
var container = new CloudBlobContainer(new Uri(reader.Current.BlobContainerUrl)); | ||
var fileName = _instanceId + "-" + _fileName; | ||
var azureBlobSink = new AzureBlobSink( | ||
name => new BlobAppendReferenceWrapper(container.GetAppendBlobReference(name)), | ||
_appName, | ||
fileName, | ||
messageFormatter, | ||
_batchSize, | ||
_period); | ||
|
||
var backgroundSink = new BackgroundSink(azureBlobSink, _backgroundQueueSize); | ||
var loggerConfiguration = new LoggerConfiguration(); | ||
|
||
loggerConfiguration.WriteTo.Sink(backgroundSink); | ||
loggerConfiguration.MinimumLevel.ControlledBy(new WebConfigurationReaderLevelSwitch(reader, | ||
configuration => | ||
{ | ||
return configuration.BlobLoggingEnabled ? configuration.BlobLoggingLevel : LogLevel.None; | ||
})); | ||
|
||
return loggerConfiguration.CreateLogger(); | ||
} | ||
|
||
} | ||
} |
111 changes: 111 additions & 0 deletions
111
src/Microsoft.Extensions.Logging.AzureWebAppDiagnostics/Internal/AzureBlobSink.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.WindowsAzure.Storage; | ||
using Serilog.Core; | ||
using Serilog.Events; | ||
using Serilog.Formatting; | ||
using Serilog.Sinks.PeriodicBatching; | ||
|
||
namespace Microsoft.Extensions.Logging.AzureWebAppDiagnostics.Internal | ||
{ | ||
/// <summary> | ||
/// The <see cref="ILogEventSink"/> implemenation that stores messages by appending them to Azure Blob in batches. | ||
/// </summary> | ||
public class AzureBlobSink : PeriodicBatchingSink | ||
{ | ||
private readonly string _appName; | ||
private readonly string _fileName; | ||
private readonly ITextFormatter _formatter; | ||
private readonly Func<string, ICloudAppendBlob> _blobReferenceFactory; | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="AzureBlobSink"/> | ||
/// </summary> | ||
/// <param name="blobReferenceFactory">The container to store logs to.</param> | ||
/// <param name="appName">The application name to use in blob path generation.</param> | ||
/// <param name="fileName">The last segment of blob name.</param> | ||
/// <param name="formatter">The <see cref="ITextFormatter"/> for log messages.</param> | ||
/// <param name="batchSizeLimit">The maximum number of events to include in a single batch.</param> | ||
/// <param name="period">The time to wait between checking for event batches.</param> | ||
public AzureBlobSink(Func<string, ICloudAppendBlob> blobReferenceFactory, | ||
string appName, | ||
string fileName, | ||
ITextFormatter formatter, | ||
int batchSizeLimit, | ||
TimeSpan period) : base(batchSizeLimit, period) | ||
{ | ||
if (appName == null) | ||
{ | ||
throw new ArgumentNullException(nameof(appName)); | ||
} | ||
if (fileName == null) | ||
{ | ||
throw new ArgumentNullException(nameof(fileName)); | ||
} | ||
if (formatter == null) | ||
{ | ||
throw new ArgumentNullException(nameof(formatter)); | ||
} | ||
if (batchSizeLimit <= 0) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(batchSizeLimit), $"{nameof(batchSizeLimit)} should be a positive number."); | ||
} | ||
if (period <= TimeSpan.Zero) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(period), $"{nameof(period)} should be longer than zero."); | ||
} | ||
|
||
_appName = appName; | ||
_fileName = fileName; | ||
_formatter = formatter; | ||
_blobReferenceFactory = blobReferenceFactory; | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override async Task EmitBatchAsync(IEnumerable<LogEvent> events) | ||
{ | ||
var eventGroups = events.GroupBy(GetBlobKey); | ||
foreach (var eventGroup in eventGroups) | ||
{ | ||
var key = eventGroup.Key; | ||
var blobName = $"{_appName}/{key.Item1}/{key.Item2:00}/{key.Item3:00}/{key.Item4:00}/{_fileName}"; | ||
|
||
var blob = _blobReferenceFactory(blobName); | ||
|
||
Stream stream; | ||
try | ||
{ | ||
stream = await blob.OpenWriteAsync(); | ||
} | ||
// Blob does not exist | ||
catch (StorageException ex) when (ex.RequestInformation.HttpStatusCode == 404) | ||
{ | ||
await blob.CreateAsync(); | ||
stream = await blob.OpenWriteAsync(); | ||
} | ||
|
||
using (var writer = new StreamWriter(stream)) | ||
{ | ||
foreach (var logEvent in eventGroup) | ||
{ | ||
_formatter.Format(logEvent, writer); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private Tuple<int,int,int,int> GetBlobKey(LogEvent e) | ||
{ | ||
return Tuple.Create(e.Timestamp.Year, | ||
e.Timestamp.Month, | ||
e.Timestamp.Day, | ||
e.Timestamp.Hour); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Null check for the new arg. You'll get a null ref otherwise when you create the FileLoggerProvider