简体   繁体   English

使用 Serilog 记录 SqlCommand CommandText 和参数以及 SqlDataReader 字段的最佳/最短方法是什么?

[英]What's the best/shortest way to log SqlCommand CommandText and Parameters and SqlDataReader fields with Serilog?

I would like to log the CommandText and the Parameters (only names and values) from a SqlCommand.我想从 SqlCommand 记录 CommandText 和参数(仅名称和值)。
I would like to log even the fields names with values from a SqlDataReader.我什至想用来自 SqlDataReader 的值记录字段名称。
I've tried with the following code but the command log is too verbose with a lot of unnecessary properties and the reader log doesn't enumerate all the fields, just the total count.我已经尝试使用以下代码,但命令日志过于冗长,包含许多不必要的属性,并且阅读器日志没有枚举所有字段,只是枚举总数。
There is some way I can customize the output?有什么方法可以定制 output? Or somebody have already created an extension for this?或者有人已经为此创建了一个扩展?

using (var connection = new SqlConnection("myconnectionstring"))
using (var command = new SqlCommand("select somefield from sometable where anotherfield = @filtername", connection))
{
    command.Parameters.AddWithValue("filtername", "somevalue");
    connection.Open();
    SqlDataReader reader;
    try
    {
        reader = command.ExecuteReader();
    }
    catch (Exception ex)
    {
        Log.Error(ex, "Error on ExecuteReader {@command}", command);
        return;
    }
    while (reader.Read())
    {
        try
        {
            /* some code */
        }
        catch (Exception ex)
        {
            Log.Error(ex, "Error on reading {@reader}", reader);
        }
    }
    reader.Close();
}

Write your own logging class that declares objects that only have the properties you are interested in logging and then pass those objects into the call to SeriLog.编写您自己的日志记录 class,声明仅具有您对日志记录感兴趣的属性的对象,然后将这些对象传递给对 SeriLog 的调用。 This code illustrates the principle only, it was not tested or guaranteed to be free of syntax / logic errors.此代码仅说明原理,未经过测试或保证没有语法/逻辑错误。

Custom logging class自定义日志记录 class

public static class MyCustomLogger {

  private class MyCustomSqlException {
    public string Message {get; set;}
    public string Query {get;set;}
    public Dictionary<string,string> Parameters = new Dictionary<string, string>();
  }

  private class MyCustomSqlReaderException {
    public string Message {get; set;}
    public Dictionary<string,string> Fields = new Dictionary<string, string>();
  }

  public static void LogSqlException(
    Exception ex, 
    string query, 
    System.Data.SqlClient.SqlParameterCollection Parameters parameters) {

    MyCustomSqlException myex = new MyCustomSqlException()
    {
      Message = ex.Message,
      Query = query
    }

    foreach(System.Data.SqlClient.SqlParameter p in parameters) {
      Parameters.Add(p.ParameterName, p.Value.ToString());
    }

    Log.Error("A SQL exception occurred", myex);
  }

  public static void LogSqlReaderException(
    Exception ex, 
    SqlDataReader reader) {

    MyCustomSqlReaderException myex = new MyCustomSqlReaderException()
    {
      Message = ex.Message
    }

    for(int x = 0; x < reader.FieldCount; x++) {
      Fields.Add(reader.GetName(x), reader.GetValue(x).ToString());
    }

    Log.Error("A SQL reading exception occurred", myex);
  }
}

Implementation执行

using (var connection = new SqlConnection("myconnectionstring"))
using (var command = new SqlCommand("select somefield from sometable where anotherfield = @filtername", connection))
{
    command.Parameters.AddWithValue("filtername", "somevalue");
    connection.Open();
    SqlDataReader reader;
    try
    {
        reader = command.ExecuteReader();
    }
    catch (Exception ex)
    {
        MyCustomLogger.LogSqlException(ex, command.CommandText, command.Parameters);
        return;
    }
    while (reader.Read())
    {
        try
        {
            /* some code */
        }
        catch (Exception ex)
        {
            MyCustomLogger.LogSqlReaderException(ex, reader);
        }
    }
    reader.Close();
}

For now I'm going to use something like this, but I found awkward have to write all this code everytime.现在我将使用这样的东西,但我发现每次都必须编写所有这些代码很尴尬。
I'm still looking for a better way.我还在寻找更好的方法。

try
{
    using (SqlDataReader dr = com.ExecuteReader())
        while (dr.Read())
        {
            try
            {
                /* some code */
            }
            catch (Exception ex)
            {
                Log.Debug(ex, "{@fiels}", Enumerable.Range(0, dr.FieldCount).ToDictionary(i => dr.GetName(i), i => dr.GetValue(i)));
                throw new ApplicationException(null, ex);
            }
        }
}
catch (Exception ex)
{
    Log.Error(ex, "{query}\n{@parameters}", com.CommandText, com.Parameters.Cast<SqlParameter>().ToDictionary(x => x.ParameterName, x => x.Value));
}

Edit 1: and this is for DataRow logging编辑 1:这是用于 DataRow 日志记录

DataTable dt = /* some logic to load dt */
foreach (DataRow dr in dt.Rows)
{
    try
    {
        /* some code */
    }
    catch (Exception ex)
    {
        Log.Debug(ex, "{@items}", dr.Table.Columns.Cast<DataColumn>().ToDictionary(x => x.ColumnName, x => dr[x.ColumnName]));
    }
}

Edit 2: I'm using this extensions now but I'm still not happy编辑 2:我现在正在使用这个扩展,但我仍然不开心

static class SerilogExtensions
{
    public static Dictionary<string, object> Log(this SqlParameterCollection p)
    {
        return p.Cast<SqlParameter>().ToDictionary(x => x.ParameterName, x => x.Value);
    }

    public static Dictionary<string, object> Log(this SqlDataReader dr)
    {
        return Enumerable.Range(0, dr.FieldCount).ToDictionary(i => dr.GetName(i), i => dr.GetValue(i));
    }

    public static Dictionary<string, object> Log(this DataRow dr)
    {
        return dr.Table.Columns.Cast<DataColumn>().ToDictionary(x => x.ColumnName, x => dr[x.ColumnName]);
    }
}

/* usage */
Log.Error(ex, "{query}\n{@parameters}", com.CommandText, com.Parameters.Log());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM