【问题标题】:Best Practices - Stored Procedure Logging最佳实践 - 存储过程日志记录
【发布时间】:2019-01-05 12:15:55
【问题描述】:

如果您有一个长时间运行的 SP,您是以某种方式记录它的操作还是只是等待此消息?

“命令成功完成。”

我认为,关于这个主题可以有很多解决方案,但是有没有最佳实践——一个经常使用的简单解决方案?

编辑

我发现了一个关于这个主题的有趣链接

http://weblogs.sqlteam.com/brettk/archive/2006/09/21/12391.aspx

文章描述了使用日志表,但存在问题

日志记录过程必须在任何事务之外执行

我无法在外部调用该插入,因为我使用光标并在每一行的该表中插入一行。

有什么想法吗?

编辑 2

挖掘..

SQL Server 中有一个xp_logevent。你试过了吗?

SQL Server Profiler怎么样?

还有Creating Log file for Stored Procedure

【问题讨论】:

  • 任何 sp,不仅仅是几个选择和更新。例如,如果您使用光标并且需要查看进度。我的问题有什么问题吗?

标签: sql-server logging


【解决方案1】:

你是如何调用存储过程的?如果是通过Management Studio,那么您可以轻松打印出如下消息

RAISERROR ('Some debugging info', 0, 1) WITH NOWAIT

这比使用PRINT 更可取,因为消息会立即出现。这些消息也可以在 ADO.NET 中通过为Connection.InfoMessage 事件连接处理程序来捕获。

我看到您已经将 SQL Profiler 列为一种可能性。您可能有兴趣知道可以在 SQL Profiler 中看到的log your own user configurable events

【讨论】:

  • 我在 management studio 中调用 sp,但问题很笼统,所以如果有一种很好的日志记录方式,它也可以在应用程序中使用。我不需要 RAISERROR,因为我需要记录进程,而不是错误。 PRINT 可以是一个选项,但是如果 sp 每秒打印 1000 行呢?我已经尝试过 SQL Profiler。这是一个不错的选择,但它并不是真正的日志记录。我想这是更多的监控,探查器不是一个通用的解决方案。也许解决方案是结合这里所说的一切。
  • 不要被名称 RAISERROR 所迷惑,它不仅仅用于错误。严重性小于 10 的任何内容仅用于信息消息。显然,无论您使用什么日志框架,都需要对其进行设计,以便记录适当数量的信息。
  • 没有。它不会停止 SP,因为严重性级别很低。详情见msdn.microsoft.com/en-us/library/ms178592.aspx
  • 使用Print 在存储过程结束之前您不会收到消息(我认为 - 或者至少您必须等待它)RAISERROR ... WITH NOWAIT 避免了这个问题。事实上根据这个链接。打印语句只是“严重级别为 0 的消息”。 sommarskog.se/error-handling-I.html
  • 打印输出是缓冲的,因此您不会立即看到结果,并且它们可能与单独缓冲的错误流一起出现乱序。由于 raiserror 使用可以强制刷新每个语句的单独缓冲区(WITH NOWAIT 指令),因此对于大多数日志记录目的来说更好。
【解决方案2】:

为了查看事情花费了多长时间,以及前一个操作修改了多少行,我将当前日期 + 时间和最后一行计数添加到每个条目中。我使用这个程序:

CREATE PROCEDURE dbo.[Log]
    @Message NVARCHAR(512),
    @RowCount INT = null OUTPUT,
    @Delimiter NCHAR(1) = N' ',
    @PadChar NCHAR(1) = N'-'
AS
    BEGIN
        SET @RowCount = @@ROWCOUNT;

        DECLARE @LogDate AS NVARCHAR(50);
        DECLARE @RowCountPadded AS NCHAR(8);

        SET @LogDate = CONVERT(NVARCHAR(50),GETDATE(),121);
        SELECT @RowCountPadded = CASE @RowCount WHEN 0 THEN REPLICATE(@PadChar,8) ELSE REPLACE(STR(@RowCount, 8), SPACE(1), @PadChar) END; 

        SET @Message = @LogDate + @Delimiter + @RowCountPadded + @Delimiter + @Message;
        RAISERROR (@Message, 0, 1) WITH NOWAIT;
    END

因此,在您的程序中,添加这样的日志输出:

EXEC dbo.[Log] 'the message';

它产生这个:

2012-12-28 11:28:25.197 -------- the message

如果您之前执行过一些操作,您会看到破折号所在的行数。如果您需要其他的行数(例如记录到表中),您可以从过程中将其作为 OUTPUT 参数取回。

更新:如果您想创建此过程一次并在任何地方使用它,请使用此gist

-- 删除的行 ---

【讨论】:

    【解决方案3】:

    我们通常使用日志记录表并注意事务。我们几乎避免任何涉及到 SQL Server 之外的事情(例如写入文件系统、调用外部服务、.NET 程序集等)

    我们也尽量避免使用游标——你的 proc 是否可能因为效率低下而长时间运行?

    【讨论】:

    • 你能说得更具体点吗?如果您无法在事务之外插入日志表怎么办?我知道,不推荐使用游标,但它是一次性使用的脚本,没有其他方法,所以这是需要使用游标的情况。
    【解决方案4】:

    我使用这个程序

    CREATE PROCEDURE dbo.PrintLog (
        @Msg VARCHAR(2048)
        , @Option VARCHAR(100) = ''
        , @Separator VARCHAR(10) = '-'
        )
    /*
    @Option is a string containing possible values as B,A,D,T
    if you want to print separator before message, include B
    if you want to print separator after message, include A
    if you want to print date, include D
    if you want to print time, include T
    Sample: 'BAD'
    
    The order of characters does not matter. it is not case sensitive
    
    Usage:
        exec dbo.PrintLog 'Timed Log', 'T'
        exec dbo.PrintLog 'Dated Log', 'D'
        exec dbo.PrintLog 'With Separator and Time', 'BT', '><'
        exec dbo.PrintLog 'With Separator and Date', 'BAD', '*'
        exec dbo.PrintLog 'With Separator and DateTime', 'BADT', 'x'
    */
    AS
    BEGIN
        declare @tempStr varchar(100)
        set @tempStr = replicate(@Separator, 50)
        IF charindex('B', upper(@Option)) > 0
            raiserror (@tempStr, 10, 1) with nowait
    
        DECLARE @prompt VARCHAR(max) = ''
    
        IF charindex('D', upper(@Option)) > 0
            SET @prompt = convert(VARCHAR, SysDatetime(), 101) + ' '
    
        IF charindex('T', upper(@Option)) > 0
            SET @prompt = @prompt + convert(VARCHAR, SysDatetime(), 108) + ' '
        SET @prompt = @prompt + @Msg
    
        raiserror (@prompt, 10, 1) with nowait
    
        set @tempStr = replicate(@Separator, 50)
        IF charindex('A', upper(@Option)) > 0
            raiserror (@tempStr, 10, 1) with nowait
    
        RETURN
    END
    
    GO
    

    用法

        exec dbo.PrintLog 'Date and Timed Log', 'DT'
        exec dbo.PrintLog 'Dated Log', 'D'
        exec dbo.PrintLog 'With Separator and Time', 'BT', '><'
        exec dbo.PrintLog 'With Separator and Date', 'BAD', '*'
        exec dbo.PrintLog 'With Separator and DateTime', 'BADT', 'x'
    

    您还可以将参数默认值更改为您想要的值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-02
      • 1970-01-01
      • 2018-03-27
      • 1970-01-01
      • 2021-08-05
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多