Azure blob logger i ASP.NET Core

I det här inlägget visar vi en implementering av en blob logger i ASP.NET Core. Den här klassen lagrar loggar som Append Blobs i ett Azure blob-lagringskonto. Loggar kan hämtas medan de skapas, du kan se till att nya loggar inte skriver över lagrade loggar och du kan ta bort gamla loggar.

Blobbar lagras i en behållare i ditt lagringskonto, du kan gruppera loggar inuti en behållare genom att lägga till ett eller flera ”mappnamn/” före filnamnet. Du kan till exempel ange blobbnamnet till ”201902/log-20190201.log”.

Inställningar

Vår blob logger behöver en anslutningssträng och ett namn för en behållare.

public class BlobStorageOptions
{
    #region Variables

    public string ConnectionString { get; set; }
    public string ContainerName { get; set; }

    #endregion

    #region Constructors

    public BlobStorageOptions()
    {
        // Set values for instance variables
        this.ConnectionString = null;
        this.ContainerName = null;

    } // End of the constructor

    #endregion

} // End of the class

Modell

Denna modell returneras i en lista i en av metoderna i blob logger klassen.

public class BlobLog
{
    #region Variables

    public string log_date { get; set; } // yyyy-MM-ddThh:mm:ss
    public string log_name { get; set; }

    #endregion

    #region Constructors

    public BlobLog()
    {
        // Set values for instance variables
        this.log_date = null;
        this.log_name = null;

    } // End of the constructor

    public BlobLog(string log_date, string log_name)
    {
        // Set values for instance variables
        this.log_date = log_date;
        this.log_name = log_name;

    } // End of the constructor

    #endregion

} // End of the class

Gränssnitt

Detta gränssnitt visar alla metoder som måste implementeras av vår blob logger klass.

public interface IBlobLogger
{
    Task LogDebug(string blob_name, string message);
    Task LogInformation(string blob_name, string message);
    Task LogWarning(string blob_name, string message);
    Task LogError(string blob_name, string message);
    Task GetLogAsStream(string blob_name, Stream stream);
    Task<string> GetLogAsString(string blob_name);
    IList<BlobLog> GetBlobLogs(string email);
    Task<bool> LogExists(string blob_name);
    Task Delete(string blob_name);
    Task DeleteByLastModifiedDate(Int32 days);

} // End of the interface

Klass

Det här är blob logger klassen. Den sista metoden (DeleteByLastModifiedDate) används för att rensa ut äldre loggar, det är dock bättre att använda livscykelhantering i Azure för att ta bort äldre blobbar.

public class BlobLogger : IBlobLogger
{
    #region Variables

    private readonly BlobStorageOptions options;
    private readonly CloudBlobClient client;
    private readonly CloudBlobContainer container;

    #endregion

    #region Constructors

    public BlobLogger(IOptions<BlobStorageOptions> options)
    {
        // Set values for instance variables
        this.options = options.Value;

        // Get a storage account
        CloudStorageAccount account = CloudStorageAccount.Parse(this.options.ConnectionString);

        // Get a client
        this.client = account.CreateCloudBlobClient();

        // Get a container reference
        this.container = this.client.GetContainerReference(this.options.ContainerName);

        // Create a container if it doesn't exist
        this.container.CreateIfNotExists();

    } // End of the constructor

    #endregion

    #region Log methods

    public async Task LogDebug(string blob_name, string message)
    {
        if (string.IsNullOrEmpty(message) == false)
        {
            await WriteToAppendBlob(blob_name, $"{DateTime.UtcNow.ToString("o")} [DBG] {message}" + Environment.NewLine);
        }

    } // End of the LogDebug method

    public async Task LogInformation(string blob_name, string message)
    {
        if (string.IsNullOrEmpty(message) == false)
        {
            await WriteToAppendBlob(blob_name, $"{DateTime.UtcNow.ToString("o")} [INF] {message}" + Environment.NewLine);
        }

    } // End of the LogInformation method

    public async Task LogWarning(string blob_name, string message)
    {
        if (string.IsNullOrEmpty(message) == false)
        {
            await WriteToAppendBlob(blob_name, $"{DateTime.UtcNow.ToString("o")} [WRN] {message}" + Environment.NewLine);
        }

    } // End of the LogWarning method

    public async Task LogError(string blob_name, string message)
    {
        if(string.IsNullOrEmpty(message) == false)
        {
            await WriteToAppendBlob(blob_name, $"{DateTime.UtcNow.ToString("o")} [ERR] {message}" + Environment.NewLine);
        }

    } // End of the LogError method

    #endregion

    #region Get methods

    public async Task GetLogAsStream(string blob_name, Stream stream)
    {
        // Get a blob object
        CloudAppendBlob blob = this.container.GetAppendBlobReference(blob_name);

        // Download the blob to a stream
        await blob.DownloadToStreamAsync(stream);

    } // End of the GetLogAsStream method

    public async Task<string> GetLogAsString(string blob_name)
    {
        // Create a string to return
        string log = "";

        // Get a blob object
        CloudAppendBlob blob = this.container.GetAppendBlobReference(blob_name);

        // Get the append blob
        using (MemoryStream stream = new MemoryStream())
        {
            await blob.DownloadToStreamAsync(stream);
            stream.Seek(0, SeekOrigin.Begin);
            log = Encoding.UTF8.GetString(stream.ToArray());
        }

        // Return the log
        return log;

    } // End of the GetLogAsString method

    public IList<BlobLog> GetBlobLogs(string email)
    {
        // Create the variable to return
        IList<BlobLog> logs = new List<BlobLog>();

        // Get a list with blobs
        IEnumerable<IListBlobItem> blobs = this.container.ListBlobs(email + "/", true, BlobListingDetails.None);

        foreach (IListBlobItem item in blobs)
        {
            // Get a blob reference
            CloudBlob blob = (CloudBlob)item;

            // Add the blob log
            logs.Add(new BlobLog(blob.Properties.LastModified.Value.ToString("yyyy-MM-ddThh:mm:ss"), blob.Name));
        }

        // Return logs
        return logs;

    } // End of the GetBlobLogs method

    #endregion

    #region Property methods

    public async Task<bool> LogExists(string blob_name)
    {
        // Get a blob reference
        CloudBlob blob = this.container.GetBlobReference(blob_name);

        // Return a boolean
        return await blob.ExistsAsync();

    } // End of the LogExists method

    #endregion

    #region Delete methods

    public async Task Delete(string blob_name)
    {
        // Get a blob object
        CloudBlob blob = this.container.GetBlobReference(blob_name);

        // Delete blob
        await blob.DeleteIfExistsAsync();
            
    } // End of the Delete method

    public async Task DeleteByLastModifiedDate(Int32 days)
    {
        // Get a list with blobs
        BlobResultSegment blob_segment = await this.container.ListBlobsSegmentedAsync("", true, BlobListingDetails.All, 100, null, null, null);
 
        // Set the date treshold
        DateTime treshold = DateTime.UtcNow.AddDays(days * -1);

        // Create an endless loop
        while(true)
        {
            // Delete blobs
            foreach (IListBlobItem item in blob_segment.Results)
            {
                // Get a blob reference
                CloudBlob blob = (CloudBlob)item;

                // Delete a blob if it is older than the threshold
                if(blob.Properties.LastModified.Value.UtcDateTime < treshold)
                {
                    await blob.DeleteIfExistsAsync();
                }
            }

            // Check if more blobs can be found
            if(blob_segment.ContinuationToken != null)
            {
                blob_segment = await this.container.ListBlobsSegmentedAsync("", true, BlobListingDetails.All, 100, blob_segment.ContinuationToken, null, null);
            }
            else
            {
                break;
            }
        }

    } // End of the DeleteByLastModifiedDate method

    #endregion

    #region Helper methods

    private async Task WriteToAppendBlob(string blob_name, string log)
    {
        // Get a blob reference
        CloudAppendBlob blob = this.container.GetAppendBlobReference(blob_name);

        // Create a blob if it doesn't exist
        if (await blob.ExistsAsync() == false)
        {
            await blob.CreateOrReplaceAsync();
        }

        // Append the log to a blob
        using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(log)))
        {
            // Append text to the blob
            await blob.AppendBlockAsync(stream);
        }

    } // End of the WriteToAppendBlob method

    #endregion

} // End of the class

Livscykelhantering

Policyn nedan kan användas för att ta bort äldre loggar, denna policy raderar blobbar som är äldre än 30 dagar i behållarna fortnox-logs och test-fortnox-logs.

{
    "rules": [
        {
            "enabled": true,
            "name": "delete-old-blobs",
            "type": "Lifecycle",
            "definition": {
                "actions": {
                    "baseBlob": {
                        "delete": {
                            "daysAfterModificationGreaterThan": 30
                        }
                    }
                },
                "filters": {
                    "blobTypes": [
                        "blockBlob",
                        "appendBlob"
                    ],
                    "prefixMatch": [
                        "fortnox-logs",
                        "test-fortnox-logs"
                    ]
                }
            }
        }
    ]
}

Lämna ett svar

E-postadressen publiceras inte. Obligatoriska fält är märkta *