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"
]
}
}
}
]
}