Sonntag , 25 Oktober 2020

Der SQL Azure Trace Listener

Der TableStorageTraceListener, über den ich bereits im Blog Post "Ein Trace Listener für den Windows Azure Table Storage" berichtet hatte, hat mich dazu inspiriert einen Trace Listener für SQL Azure zu schreiben.
Den dabei entstandenen "Ersten Entwurf", werde ich in diesem Blog Post kurz vorstellen.

 

Die Code Basis

Um das Rad nicht komplett neu erfinden zu müssen, habe ich den TableStorageTraceListener als Code-Basis für den SQL Azure Trace Listener genutzt.

Als O/R-Mapper kommt die aktuelle Version des Entity Frameworks zum Einsatz, die ich den Projekten mittels NuGet hinzugefügt habe.

 

Die LogEntry Klasse

Zu Beginn habe ich, in der AzureDiagnostics Klassenbibliothek, die LogEntry Klasse angepasst:

Die Vererbung, von der TableServiceEntity Klasse, war nicht mehr nötig, deshalb habe ich diese entfernt.
Da jetzt aber der Primärschlüssel, sowie die Timestamp Eigenschaft, fehlten, wurde dieser ergänzt.
Als neuer Primärschlüssel dient nun die LogEntryId Eigenschaft.
Anschließend würde die Klasse und deren Eigenschaften mit Attributen aus dem System.ComponentModel.DataAnnotations dekoriert, um später das EntityFramework gezielter nutzen zu können.

[Table("DevLogsTable")]
public class LogEntry
{
  [Key]
  public long LogEntryId { get; set; }
  [Required]
  public long EventTickCount { get; set; }
  [Required]
  public int Level { get; set; }
  [Required]
  public int EventId { get; set; }
  [Required]
  public int Pid { get; set; }
  [Required]
  [StringLength(50)]
  public string Tid { get; set; }
  [Required]
  [StringLength(255)]
  public string RoleName { get; set; }
  [Required]
  [StringLength(255)]
  public string RoleId { get; set; }
  [Required]
  [MaxLength()]
  public string Message { get; set; }
  [Required]
  public DateTime Timestamp { get; set; }
}

 

Die LogContext Klasse

Für den Code-First Ansatz des Entity Frameworks, wurde eine Datenbankkontext-Klasse hinzugefügt (LogContext).

Diese enthält nur eine Collection der LogEntry Objekte, sowie einen Konstruktor, der, bei Bedarf, die Log-Tabelle in SQL Azure anlegt.

Ob es mit dem Entity Framework möglich ist, eine saubere SQL Azure Datenbank inkl. Tabellen erzeugen zu können, habe ich bislang nicht ausprobiert.
Deshalb lasse ich auch sicherheitshalber die Log-Tabelle im Konstruktor der Datenbankkontext -Klasse anlegen.

public class LogContext : DbContext
{
  public LogContext(string connectionString)
    : base(new SqlConnection(connectionString), true)
  {
    // Erzeuge die Tabelle, wenn sie nicht existiert.
    this.Database.ExecuteSqlCommand(
      "IF NOT EXISTS (SELECT * FROM sys.objects "
      + "WHERE object_id = OBJECT_ID(N'[dbo].[DevLogsTable]') "
      + "AND type in (N'U')) "
      + "CREATE TABLE [dbo].[DevLogsTable]("
      + "[LogEntryId] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,"
      + "[EventTickCount] [bigint] NOT NULL,"
      + "[Level] [int] NOT NULL,"
      + "[EventId] [int] NOT NULL,"
      + "[Pid] [int] NOT NULL,"
      + "[Tid] [nvarchar](50) NOT NULL,"
      + "[RoleName] [nvarchar](255) NOT NULL,"
      + "[RoleId] [nvarchar](255) NOT NULL,"
      + "[Message] [nvarchar](max) NOT NULL,"
      + "[Timestamp] [datetime] NOT NULL);");
  }

  public DbSet<LogEntry> LogEntries { get; set; }
}

 

Der SqlAzureTraceListener

Neben einigen kleineren Anpassungen (Namensänderungen und Entfernen von nicht mehr benötigtem Code), wurden die „größten“ Änderungen bei den Methode Initialize und Flush durchgeführt.

In der Initialize Methode wird ein neues Datenbankkontext-Objekt erzeugt, welches, bei Bedarf, die Log-Tabelle anlegt.

private void Initialize()
{
  string connectionString = 
    RoleEnvironment.GetConfigurationSettingValue(
    this.connectionStringName);
  context = new LogContext(connectionString);
  this.isInitialized = true;
}

Die Flush Methode initialisiert beim ersten Aufruf den Trace Listener und schreibt die im Buffer (traceLog) enthaltenen Log-Einträge, mittels Datenbankkontext-Objekt, in die Datenbank.

public override void Flush()
{
  if (!this.isInitialized)
  {
    lock (this.initializationSection)
    {
      if (!this.isInitialized)
      {
        Initialize();
      }
    }
  }

  lock (this.traceLogAccess)
  {
    this.traceLog.ForEach(entry => context.LogEntries.Add(entry));
    this.traceLog.Clear();
  }

  context.SaveChanges();
}

 

Änderungen am Demoprojekt für die Cloud

Um das Demoprojekt in der Cloud zu betreiben, muss nur der SQL Azure Connection String in der ServiceConfiguration.Cloud.cscfg angepasst werden.
Alternativ kann man hierzu auch den Dialog für die Rolleneinstellungen nutzen:

Cloud Projekt - Rolleneigenschaften

 


Download Download der Beispielanwendung:

Weitere Informationen unter: