1. Copy the script.
2. Create the procedure
3. Run the script.
--------------------------------------------------------------------
exec usp_JOBSTEPLOG_CHECK
1. Copy the script.
2. Create the procedure
3. Run the script.
--------------------------------------------------------------------
exec usp_JOBSTEPLOG_CHECK
USE [DBMaint] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO --------------------------------------------------------------------------------------------------------------------- Create Proc [dbo].[usp_JOBSTEPLOG_CHECK] (@dbmail_profile SYSNAME = NULL, @dbmail_recipient SYSNAME = NULL) as /* Purpose: The Purpose of this script is to run against - 1.It will first check the path of the JOBOUTPUT folder, If itÆs deleted/missing/renamed, it will create a fresh folder with name ôJOBOUTPUTö to create the output of each job to that folder. 2.Identifies Missing OUTPUT FILE and fixes all 3.IF any Job has wrong OUTPUT Path, identifies and fixes them 4.IF WRONG path is mentioned in the JOB OUTPUT, it will fix them 5.It will send an email alert if incase, it identifies any Missing OUTPUT file against any of the JOB steps. --------------------------------------------------------------------------------------------------------------------- *** RUNNING Instructions:- exec usp_JOBSTEPLOG_CHECK @dbmail_profile= ' ', @dbmail_recipient = ' '; --------------------------------------------------------------------------------------------------------------------- ... */ BEGIN SET nocount ON BEGIN try DECLARE @output_file_name VARCHAR(max) -- Name of log DECLARE @STEP_NAME VARCHAR(MAX) -- Step name DECLARE @path VARCHAR(max) -- Path for working directory DECLARE @path1 VARCHAR(max) -- Path for working directory DECLARE @JOB_ID VARCHAR(max) -- Job ID DECLARE @STEP_ID VARCHAR(MAX) -- Step_ID DECLARE @JOB_Name VARCHAR(max) -- Job ID DECLARE @CMD VARCHAR(MAX) -- EXECUTABLE COMMAND DECLARE @CMD1 VARCHAR(4000) -- EXECUTABLE COMMAND DECLARE @INSTANCE VARCHAR(MAX) -- GRABS INSTANCE NAME TO DIFFERENTIATE REPORT NAMES DECLARE @path2 VARCHAR(max) DECLARE @RC INT DECLARE @RC1 INT DECLARE @NumRecords INT DECLARE @Count INT DECLARE @result VARCHAR(4000) set nocount on declare @file_string varchar(4000) -------------- Running Procedsure Details --------------------------- declare @servername nvarchar(4000) = (Select @@servername); DECLARE @ProcedureName SYSNAME; SET @ProcedureName = OBJECT_NAME(@@PROCID); Print 'Running Procedure:= ' + @ProcedureName ----------------------------------------------------------------------- DECLARE @DEFAULTS1 TABLE (VALUE VARCHAR(MAX), DATA VARCHAR(MAX)) INSERT INTO @DEFAULTS1 EXEC @rc1 = master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',N'Software\Microsoft\MSSQLServer\MSSQLServer',N'BackupDirectory' SELECT @PATH2 = DATA FROM @DEFAULTS1 WHERE VALUE = 'BackupDirectory' SELECT @PATH2 = @PATH2 + '\JOBOUTPUT\' --PRINT @PATH2 SET @file_string = @PATH2 --SET @Path = (SELECT @file_string) create table #xp_fileexist_output ( [FILE_EXISTS]intnot null, [FILE_IS_DIRECTORY]intnot null, [PARENT_DIRECTORY_EXISTS]intnot null) insert into #xp_fileexist_output exec master.dbo.xp_fileexist @file_string if exists ( select * from #xp_fileexist_output where FILE_IS_DIRECTORY = 1 ) begin print 'File is a directory = ' +quotename(@file_string) end else begin print 'File is not a directory = '+quotename(@file_string) --checking if dir exist if not create it SELECT @CMD1 = 'dir' + @file_string EXEC @result = master.dbo.xp_cmdshell @CMD1, NO_OUTPUT IF @result 0 BEGIN SELECT @CMD1 = 'mkdir ' + @PATH2 EXEC master.dbo.xp_cmdshell @CMD1, NO_OUTPUT Print 'Folder is created successfully := ' +@PATH2 END end --select * from #xp_fileexist_output create table #Missing_Job_Output ( [JOB NAME]nvarchar(4000)null, [output_file_name]nvarchar(4000) null, [JOB Step Name]nvarchar(4000)null, [DB NAME] nvarchar(4000) null, [last_run_outcome] nvarchar(100) NULL, [last_run_datetime] datetime NULL ) -- SELECT --@@servername as ServerName, --name as JobName, --date_modified as LastModifiedDate --FROM --msdb.dbo.sysjobs WITH (NOLOCK) --WHERE --enabled = 0 -- Count Number Of files where the OUTPUT files are MISSING...& Ignore Disabled Jobs. SET @Count = (select COUNT(*) from msdb.dbo.sysjobs j inner join msdb.dbo.sysjobsteps js on j.job_id = js.job_id WHERE js.output_file_name IS NULL and enabled = 1); PRINT 'Number of Active & Enabled JOBS where Output files are Missing are:= ' + CAST (@Count as VARCHAR(10)) PRINT 'No DBMAIL will be Triggerred.' IF @Count >0 BEGIN -- Script to find the Jobs Running with NO OUTPUT File. INSERT Into #Missing_Job_Output select j.name AS JOBNAME,js.output_file_name, js.step_name,js.database_name "Executing On which DB?", last_run_outcome = case when js.last_run_outcome = 0 then 'Failed' when js.last_run_outcome = 1 then 'Succeeded' when js.last_run_outcome = 2 then 'Retry' when js.last_run_outcome = 3 then 'Canceled' else 'Unknown' end, last_run_datetime = msdb.dbo.agent_datetime( case when js.last_run_date = 0 then NULL else js.last_run_date end, case when js.last_run_time = 0 then NULL else js.last_run_time end) from msdb.dbo.sysjobs j inner join msdb.dbo.sysjobsteps js on j.job_id = js.job_id WHERE js.output_file_name IS NULL order by js.output_file_name; END -- Update All the OutPut Files with NULL Value. --UPDATE msdb.dbo.sysjobsteps SET -- OUTPUT_FILE_NAME = null --SELECT @NumRecords = @@ROWCOUNT --PRINT 'TOTAL No Of SQL Jobs In the Instance:= ' + CAST(@NumRecords as varchar(10)) +CHAR(13) --PRINT @NumRecords IF (SELECT Cursor_status('global', 'db_cursor')) >= -1 BEGIN IF (SELECT Cursor_status('global', 'db_cursor')) > -1 BEGIN CLOSE db_cursor END DEALLOCATE db_cursor END -- Doing this step because, if incase someone provides a wrong path to job or changes the job path. UPDATE msdb.dbo.sysjobsteps SET OUTPUT_FILE_NAME = null DECLARE db_cursor CURSOR FOR SELECT STEP_NAME, JOB_ID, STEP_ID, STEP_NAME --, output_file_name FROM msdb.dbo.sysjobsteps where output_file_name is null OPEN db_cursor FETCH NEXT FROM db_cursor INTO @STEP_NAME, @JOB_ID, @STEP_ID, @STEP_NAME SELECT 'STEPNAME' = @STEP_NAME, 'JOBID' = @JOB_ID, 'JOBNAME' = @JOB_NAME, 'STEPID' = @STEP_ID, 'STEPNAME' = @STEP_NAME, 'PATH' = @Path, 'OUTPUT' = @OUTPUT_FILE_NAME, 'INSTANCE' = @INSTANCE WHILE @@FETCH_STATUS = 0 BEGIN /* DECLARE @DEFAULTS TABLE (VALUE VARCHAR(MAX), DATA VARCHAR(MAX)) --SELECT @PATH = @SOURCEPTH INSERT INTO @DEFAULTS EXEC @rc = master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',N'Software\Microsoft\MSSQLServer\MSSQLServer',N'BackupDirectory' -- Print the Job Stack --SELECT * from @DEFAULTS SELECT @PATH = DATA FROM @DEFAULTS WHERE VALUE = 'BackupDirectory' SELECT @PATH = @PATH + '\JOBOUTPUT\' */ --SELECT @PATH2 SET @INSTANCE = (SELECT @@Servername) SET @STEP_NAME = @STEP_NAME + '.TXT' -- SELECT @STEP_NAME AS STEPNAME --, @JOB_ID, @STEP_ID, @STEP_NAME SET @OUTPUT_FILE_NAME = @PATH2 + @STEP_NAME -- SELECT @OUTPUT_FILE_NAME /* SELECT 'STEPNAME' = @STEP_NAME, 'JOBID' = @JOB_ID, 'STEPID' = @STEP_ID, 'STEPNAME' = @STEP_NAME, 'PATH' = @Path, 'OUTPUT' = @OUTPUT_FILE_NAME, 'INSTANCE' = @INSTANCE */ SET @CMD = 'update msdb.dbo.sysjobsteps set output_file_name = '''+@OUTPUT_FILE_NAME+ ''' where job_id = ''' + @JOB_ID+''' and step_id = ' + @STEP_ID +'' -- PRINT @CMD EXEC(@CMD) SET @OUTPUT_FILE_NAME = '' FETCH NEXT FROM db_cursor INTO @STEP_NAME, @JOB_ID, @STEP_ID, @STEP_NAME END CLOSE db_cursor DEALLOCATE db_cursor --SET NOCOUNT OFF --select @body1 DECLARE @body nVARCHAR(max) DECLARE @subject VARCHAR(max) DECLARE @profile_name VARCHAR(max) DECLARE @recipients VARCHAR(max) -- Print @count IF @count > 0 BEGIN SELECT @subject = 'CURRENTLY THERE ARE JOB(S) IN THE SERVER WHICH HAD MISSING/WRONG JOB OUTPUT FILES : ' + ' '+ Substring(@@servername, 1, 20) set @body = N'<H3> <Font Color="red"> CURRENTLY THERE ARE JOBS IN THE SERVER WHICH HAD **** MISSING/WRONG JOB OUTPUT FILES **** FOUND : : </font> </H3>' + N'<H4> <Font Color="Red"> Server: ' + @@servername + '</H4></font>' + ' '+ '<BR>'+ N'<table border="1">' + N'<tr> <th>JOB NAME</th> <th>Output File Name</th> <th>Step Name</th> <th>Executed On DB</th> <th>Last Run Date & Time</th>' + CAST ( ( SELECT td = ISNULL(CONVERT(varchar(200),[JOB NAME]), 'NULL'), '', td = ISNULL(CONVERT(varchar(200),[output_file_name]), 'NULL'), '', td = ISNULL(CONVERT(varchar(200),[JOB Step Name]), 'NULL'), '', td = ISNULL(CONVERT(varchar(200),[DB NAME]), 'NULL'), '', td = ISNULL(CONVERT(nvarchar(200),[last_run_datetime],106), 'NULL'), ' ' from #Missing_Job_Output order by [output_file_name] FOR XML PATH('tr'), TYPE ) AS NVARCHAR(MAX) ) + N'</table>' + ' ' + '<Br><H3> <Font Color="darkblue"> The Issue is fixed. Missing OutPut File Against the JOB STEP(s) Is/Are Re-Created Automatically.. ' + '</H4></font>' + ' '+ '<BR>'; select @body IF (@dbmail_profile IS NOT NULL) OR (@dbmail_recipient IS NOT NULL) BEGIN -- Sending Email to Recipients. EXEC msdb.dbo.Sp_send_dbmail @profile_name = @dbmail_profile, @recipients = @dbmail_recipient, @subject = @subject, @body = @body, @body_format = 'HTML', @importance = 'HIGH' END END /* Test Queued Email. ... Query 1 : SELECT [profile_id] ,[name] ,[description] ,[last_mod_datetime] ,[last_mod_user] ,'EXEC msdb.dbo.sp_send_dbmail @profile_name = ''' + name + ''', @recipients = '' '', @subject = ''Test'', @body = ''Message'', @body_format = ''HTML'';' AS TestSQL FROM [msdb].[dbo].[sysmail_profile] Query 2: EXEC MSDB..sysmail_help_queue_sp @queue_type = 'Mail' ; Query 3: SELECT * FROM msdb..sysmail_mailitems WHERE sent_date > DATEADD(DAY, -1,GETDATE()) */ --EXEC msdb.dbo.sysmail_add_profile_sp -- @profile_name = @dbmail_profile, -- @recipients = @dbmail_profile, -- @body = @body, -- @subject = @subject SET NOCOUNT OFF END try BEGIN catch DECLARE @ErrorNumber INT; DECLARE @ErrorSeverity INT; DECLARE @ErrorState INT; DECLARE @ErrorLine INT; DECLARE @ErrorProcedure NVARCHAR(4000); DECLARE @ErrorMessage NVARCHAR(4000); SELECT @ErrorNumber = Error_number(), @ErrorSeverity = Error_severity(), @ErrorState = Error_state(), @ErrorLine = Error_line(), @ErrorProcedure = Error_procedure(); SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + Error_message(); SELECT @ErrorMessage AS [Error_Message]; SELECT @ErrorProcedure AS [Error_Procedure]; PRINT 'Error ' + CONVERT(VARCHAR(50), Error_number()) + ', Severity ' + CONVERT(VARCHAR(5), Error_severity()) + ', State ' + CONVERT(VARCHAR(5), Error_state()) + ', Procedure ' + Isnull(Error_procedure(), '-') + ', Line ' + CONVERT(VARCHAR(5), Error_line()); PRINT Error_message(); RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState) WITH SETERROR, Log, NOWAIT; Return; END catch SET nocount OFF END GO