Brett Kaiser (x002548) Blog

Not Just a Number - Brett Kaiser

Stored Procedure Logging

Every so often, someone asks, "How do I know who executed a SQL Statement against my database".

Well you can either have SQL Profiler running all the time (which can be very expensive), or you can use Lumingent's Log Explorer.

I have taken a different tack lately.

Any Access to a database I am supporting will be done ONLY Through stored procedures.  OK, that's not "lately", but the part I've added is that the developers MUST call the sproc below.  What this does is to log every stored procedure call.  I now have statistics as to what's being called when, and how long the operation takes.  There are several benefits, but the best being that I can see which developers don't have their thinking cap on in dev, and we can proactiveley review these sprocs.

OK, now you say, how do you know that developer is callin the logging sproc, and the short answer is, that in dev, I don't.  However, No sprocs get moved to QA without a reivew by me or someone on the DBA team.  Once it's in QA, they can't touch the code.  After QA Sign off, the that code gets moved to UAT, then up to PROD.

In any case I find it useful, and the execution of the logging is in the microseconds.  I guess the downside of a app that gets slammed with millions of hits is that this could add up and affect performance, but shy of that, we have not noticed any impact.  In any case, for what it's worth here's the sproc and the table DDL.  One other note, I do this on other platforms as well, for example DB2 OS/390...only problem there is the need to monitor the tablespace for this table, since you coul blow out on extents...which is not really a problem on SQL Server...except that you potentially could run out of disk space...In either case, you need to monitor that, and archive the data.  Any comments are appreciated.

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[usp_LogProcCalls]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[usp_LogProcCalls]
GO

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO


CREATE PROCEDURE [dbo].[usp_LogProcCalls] (
   @SprocName  sysname
 , @TranStart  datetime
 , @TranEnd  datetime
 , @Rows  int
 , @Err   int
 , @Paramters varchar(255)
 , @rc   int OUTPUT)
AS
SET NOCOUNT ON

Declare @error int, @RowCount int, @Error_Message varchar(255), @Error_Type int, @Error_Loc int

BEGIN TRAN
 DECLARE @LogStart datetime
 SELECT @rc = 0, @LogStart = GetDate()
  IF (SELECT @@TRANCOUNT) <> 1
   BEGIN
    SELECT @Error_Loc = 1
         , @Error_Message =  'The logging procedure must be executed outside of any transaction.  @@TRANSCOUNT='
   + CONVERT(varchar(5),@@TRANCOUNT)
         , @Error_Type = 50002, @rc = -6661
    GOTO usp_LogProcCalls_Error
   END

 INSERT INTO Sproc_Log (
   [SprocName]
 , [TranStart]
 , [TranEnd]
 , [LogStart]
 , [LogEnd]
 , [Rows]
 , [Err]
 , [Paramters])
 SELECT
   @SprocName
 , @TranStart
 , @TranEnd
 , @LogStart
 , GetDate()
 , @Rows
 , @Err
 , @Paramters

  Select @RowCount = @@ROWCOUNT, @error = @@error
 
  IF @error <> 0
   BEGIN
    SELECT @Error_Loc = 2, @Error_Type = 50001, @rc = -6662
    GOTO usp_LogProcCalls_Error
   END
 
  IF @RowCount <> 1
   BEGIN
    SELECT @Error_Loc = 3
         , @Error_Message =  'Expected 1 row to be inserted in to the sproc log.  Actual Number inserted = '
   + CONVERT(varchar(5),@RowCount)
         , @Error_Type = 50002, @rc = -6663
    GOTO usp_LogProcCalls_Error
   END

COMMIT TRAN

usp_LogProcCalls_Exit:

RETURN

usp_LogProcCalls_Error:

Rollback TRAN

If @Error_Type = 50001
 BEGIN

  Select @error_message = (Select 'Location: ' + ',"' + RTrim(Convert(char(3),@Error_Loc)) 
          + ',"' + '  @@ERROR: ' + ',"' + RTrim(Convert(char(6),error))
          + ',"' + ' Severity: ' + ',"' + RTrim(Convert(char(3),severity))
          From master..sysmessages
        Where error = @error)
 END

If @Error_Type = 50002

 BEGIN
  Select @Error_Message = 'Location: ' + ',"' + RTrim(Convert(char(3),@Error_Loc))
                   + ',"' + ' Severity:  UserLevel '
              + ',"' + ' Message: ' + ',"' + RTrim(@Error_Message)
 END

RAISERROR @Error_Type @Error_Message

GOTO usp_LogProcCalls_Exit
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

 

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Sproc_Log]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Sproc_Log]
GO

CREATE TABLE [dbo].[Sproc_Log] (
 [SprocName] [sysname] NOT NULL ,
 [TranStart] [datetime] NOT NULL ,
 [TranEnd] [datetime] NOT NULL ,
 [SYSTEM_USER] [char] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
 [LogStart] [datetime] NOT NULL ,
 [LogEnd] [datetime] NOT NULL ,
 [Rows] [int] NOT NULL ,
 [Err] [int] NOT NULL ,
 [Paramters] [varchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO

 

 

Legacy Comments


Tom Hamilton
2006-09-25
re: Stored Procedure Logging
I like this idea very much but hit an error when compiling the sProc:

Msg 137, Level 15, State 1, Procedure usp_LogProcCalls, Line 78
Must declare the scalar variable "@error_message".


2006-09-25
re: Stored Procedure Logging
Case ! Change @error_message to @Error_Message
fixes problem

Brett
2006-09-26
re: Stored Procedure Logging
Sorry, my databases are never set up to be Case Sensitive...I mean, what's the point


pankaj
2006-12-04
re: Stored Procedure Logging
how do call a stored procedure with out parameters. i.e
Procedure proeno (Peno IN Number,
PEname IN Varchar2,
Sal OUT Number)
AS
Leno number(7);
LEname varchar2(10);
Lsal Number(10,2);
Begin
select eno,ename,sal into Leno,LEname,Lsal from emp where eno = peno;
end;
to execute this i have 2 pass values .
& wat shud the out parameter values shud be.