【问题标题】:Track or log calls to a user-defined function in SQL Server跟踪或记录对 SQL Server 中用户定义函数的调用
【发布时间】:2017-06-01 20:06:01
【问题描述】:

了解副作用运算符(如“插入”)在用户定义函数中是不允许的,如何记录(或以其他方式跟踪)对特定用户定义函数的调用?我还想捕获传递给 UDF 的参数。

理想情况下,日志将是一个表,其中插入了有关对 UDF 的每次调用的信息(时间戳和参数值)。然后可以从该表中得出报告和使用指标。

我无法将 UDF 重写为存储过程,即使是同名存储过程,也不会破坏许多需要 UDF 且我无法控制的下游系统。

我也不愿意在我们的服务器上启用任何类型的命令 shell 功能,这会削弱 SQL Server 的最佳实践安全默认值。

【问题讨论】:

  • 您不应该为此查看 SQL Server Profiler 吗?
  • @PanagiotisKanavos 我根本不推荐分析器。跟踪,也许,或者更好的扩展事件或审计。 Profiler 可以用于本地调试,但绝对不应该用于生产系统。恕我直言。
  • 您能解释一下为什么要记录对用户定义函数的所有调用吗?它不会影响答案(嗯,我不这么认为),我只是想了解您期望从中获得什么。
  • 当然,@Aaron... 有问题的 UDF 是一个报告系统的登录验证器(它传回 0 表示失败,1 表示成功,2 表示成功并使用临时密码)。我们想看看谁在尝试登录,以及他们是否成功登录。
  • 看起来类似于这个问题stackoverflow.com/questions/352605/…

标签: sql-server user-defined-functions udf


【解决方案1】:

我找到了您的问题的解决方案。这有点棘手,看起来像一个 hack,但似乎无法通过其他方式解决。

这个想法是创建一个 .NET SQL 函数,将数据记录到您需要的位置(文件、Windows EventLog、db 等),接下来创建调用此 .NET 函数的 SQL UDF,最后从您的函数中调用此 SQL 函数传递所有需要记录的参数。 SQL Server 不会检查 .net 函数内部的内容,您可以在其中编写所需的所有逻辑。

如何在没有任何安全限制的情况下创建 .net SQL 函数的想法来自 post

所以,用这个文件创建一个 .net 库项目

using System;

namespace SqlTest
{
 public class LogEvent
 {
    [Microsoft.SqlServer.Server.SqlFunction]
    public static int Log(string data)
    {
        System.IO.File.AppendAllText(@"C:\Log\LogUDF.txt", data);
        return 0;
    }
 }
}

使用一些 pfx 证书对其进行签名(项目属性 -> 签名选项卡)。

接下来,调用这个查询

USE [master]
CREATE ASYMMETRIC KEY LogKey FROM EXECUTABLE FILE =
'C:\Work\ConsoleApplication1\SqlTest\bin\Debug\SqlTest.dll'

CREATE LOGIN LogLogin FROM ASYMMETRIC KEY LogKey

GRANT UNSAFE ASSEMBLY TO LogLogin 
GO

USE [MyDB]
CREATE ASSEMBLY SqlTest FROM
'C:\Work\ConsoleApplication1\SqlTest\bin\Debug\SqlTest.dll' 
WITH PERMISSION_SET = unsafe
GO
CREATE FUNCTION dbo.Log( @data as nvarchar(200) )
RETURNS int
AS EXTERNAL NAME SqlTest.[SqlTest.LogEvent].Log

您需要在此处更改已编译库 MyDB 的路径 - 您的数据库名称。 您将创建 dbo.Log SQL 函数。接下来,您可以在需要的地方调用它。例如来自这个TestFunction

CREATE FUNCTION TestFunction
(
    @p1 int
)
RETURNS int
AS
BEGIN
    DECLARE @temp int
    SELECT @temp = [dbo].[Log] ('fff')
    RETURN 1
END

因此,调用SELECT TestFunction(1) 会将'fff' 文本写入C:\Log\LogUDF.txt 文件。

就是这样。一些重要说明:

  1. SQL 服务器应该有权限(登录名/用户)写入文件C:\Log\LogUDF.txt
  2. 您应该是 SQL Server 管理员

【讨论】:

  • 优秀的答案,@Vasyl。花了一些时间来逐步完成它,但您提供了我完成任务所需的一切。
【解决方案2】:

您可以尝试以下方法:

1) 使用 SQL Profiler 检查每个不同场景的捕获数据

检查SP:StmtCompleted 以确保您捕获在存储过程中执行的语句或使用定义的函数。还要确保包含所有必需的列(TextData、LoginName、ApplicationName 等)。 TextData 对于此解决方案至关重要。

2) 检查每个场景以查看您在分析器中收到的内容。例如:

-- a mock function that is similar to what I have understood your function does
alter FUNCTION dbo.GetLoginResult(@username VARCHAR(64))
    RETURNS INT
AS
BEGIN
    DECLARE @l INT = LEN(@username)

    IF (@l < 10)
        RETURN 0

    RETURN 1

    -- DECLARE @Result INT
    -- SELECT @Result = DATEPART(s, GETDATE()) % 3
    -- RETURN @Result
END
go

select dbo.GetLoginResult('SomeGuy')  --> `IF (@l < 10)` and `RETURN 0`
GO

select dbo.GetLoginResult('Some girl with a long name')  --> `IF (@l < 10)` and `RETURN 1`
GO

因此,如果您可以调整您的函数以在即将返回特定输出时执行特定指令的方式编写,您可以根据配置信息识别函数的结果是什么(因为输入和输出值似乎没有在分析器中捕获)

3) 服务器端跟踪

如前所述,SQL Profiler 会产生大量开销,因此您应该使用服务器端跟踪。幸运的是,您可以按照here 所示导出刚刚创建的分析信息:

i) SQL Profiler -> 文件 -> 导出 -> 脚本跟踪定义 -> 对于 SQL Server ..

ii) 替换生成脚本中的路径并运行->记住生成的id(就是trace id)

iii) 停止跟踪后,您可以在分析器中打开文件并将其数据导出到表中(它被sqlserver 进程锁定)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-05
    • 2016-11-30
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多