How to keep default logic logs in BPC 7.5 MS
Applies to
SAP Business Planning and Consolidation 7.5 version for Microsoft, SP07
Summary
Business Planning and Consolidation for Microsoft platform keeps track only of the very latest execution of default script logic. There is a requirement to save default script logic logs for each execution and keep them separately per Appset and Application.
This document will show how to do this using a service that will look after the log files.
Background Information
Whenever a user sends data to BPC default logic is triggered. Log files are saved in the following directory \PC_MS\Data\Webfolders\[APPSET]\[APPL]\PrivatePublications\[USERID]\TempFiles. The service that will be built in this document will look after the appset folder \PC_MS\Data\Webfolders\[APPSET]\ and every *.log file in it. The service will copy log files into \PC_MS\Data\Webfolders\Logs directory whenever a log file is edited.
Thus, it works the following way:
- end-user posts data
- default script logic is executed
- file with name DebugLogic.log inside \PC_MS\Data\Webfolders\[APPSET]\[APPL]\PrivatePublications\[USERID]\TempFiles is changed by BPC
- service notices the change and copies the file to \PC_MS\Data\Webfolders\Logs
- name of the file will be [APPSET]_[APPL]_[timestamp].log.
The program was built and tested on the following configuration.
- BPC 7.5 for Microsoft SP07
Step-by-Step Procedure
Create LogWatcherService
The following steps describe how to create/compile and install the service.
- The service is to be located in Appset folder (\PC_MS\Data\Webfolders\[APPSET]\)
- Go to the folder \PC_MS\Data\Webfolders\[APPSET]\ (in my example this will be D:\PC_MS\Data\Webfolders\APSHELL_COPY\)
- Create the following files in Notepad
CompileLogWatcher.bat
InstallLogWatcher.bat
LogWatcherLib.vb
LogWatcherService.vb
UninstallLogWatcher.bat
- Copy and paste the code at the bottom of this document into the corresponding files.
- Adjust InstallLogWatcher.bat and UninstallLogWatcher.bat with full directory address to the folder \PC_MS\Data\Webfolders\[APPSET]\
- Run CompileLogWatcher.bat
- Two new files will appear in the folder
- Run as administrator InstallLogWatcher.bat
- The service is created.
- Start – Run and type in Service.msc
- Find the service by the name “LogWatcher_[Appset]”
- Start the service.
The service was created and started.
Test LogWatcherService
As a prerequisite there should be valid default logic in appset/application
- Log on to BPC Excel.
- Create new or open an existing input schedule.
- Post some data.
- Go to \PC_MS\Data\Webfolders\Logs and look for a new file with the following name [APPSET]_[APPL]_[timestamp].log.
Uninstall LogWatcherService
- Run as administrator UninstallLogWatcher.bat
Notes
The filter is setup so that is looks after any changes in any file with *.log name inside directory. If it is required to look after only certain files and/or in a certain directory please change filters inside Define_Watcher procedure in LogWatcher.vb. For more information please refer to http://msdn.microsoft.com/ru-ru/library/system.io.filesystemwatcher.aspx
CompileLogWatcher.bat |
---|
Path=%path%;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\;C:\Windows\Microsoft.NET\Framework\v3.5;c:\windows\microsoft.net\framework\v4.0.30319; vbc.exe /OUT:LogWatcherLib /T:library LogWatcherLib.vb vbc.exe /OUT:LogWatcherService /T:exe LogWatcherService.vb /R:LogWatcherLib.dll PAUSE |
InstallLogWatcher.bat |
---|
Path=%path%;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\;C:\Windows\Microsoft.NET\Framework\v3.5;c:\windows\microsoft.net\framework\v4.0.30319; installutil D:\PC_MS\Data\Webfolders\ApShell\LogWatcherService.exe PAUSE |
LogWatcherLib.vb |
---|
Imports System Imports System.IO Imports Microsoft.VisualBasic Imports System.Security.Permissions Imports System.Diagnostics Imports System.Collections.Generic Imports System.Text Imports System.Text.RegularExpressions Public Class CL_Watcher Private m_WatchDirectory As String Private WithEvents m_FileSystemWatcher As FileSystemWatcher Private WatcherLogDir as string Private Watcher_Name as string Private AppsetName as string Private Application as string Private sPath As String Public Sub Define_Watcher(attr as string, Appset_Name as string) Const WatcherLog as string = "Logs" AppsetName = Appset_Name Watcher_Name = "Watcher_for_" & AppsetName sPath = attr m_WatchDirectory = sPath WriteToEventLog ("The folder" & m_WatchDirectory & " is being watched", Watcher_Name) 'check if the directory WatcherLog exists 'WatcherLogDir = m_WatchDirectory & "\" & WatcherLog WatcherLogDir = m_WatchDirectory.Substring(0, _ m_WatchDirectory.LastIndexOf("\")) & "\" & WatcherLog Dim dir As New IO.DirectoryInfo(WatcherLogDir) If dir.Exists Then 'do nothing WriteToEventLog ("The folder " & WatcherLogDir & " exists", Watcher_Name) Else Directory.CreateDirectory(WatcherLogDir) WriteToEventLog ("The log folder " & WatcherLogDir & " has been created", Watcher_Name) End If 'Make the FileSystemWatcher and watch only .log files m_FileSystemWatcher = New _ FileSystemWatcher(m_WatchDirectory, "*.log") ' Watch for changes in LastAccess and LastWrite times, and ' the renaming of files or directories. 'm_FileSystemWatcher.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName) m_FileSystemWatcher.NotifyFilter = (NotifyFilters.LastWrite) ' Add event handlers. AddHandler m_FileSystemWatcher.Changed, AddressOf OnChanged 'AddHandler m_FileSystemWatcher.Created, AddressOf OnChanged 'AddHandler m_FileSystemWatcher.Deleted, AddressOf OnChanged 'AddHandler m_FileSystemWatcher.Renamed, AddressOf OnRenamed 'Include subdirectories m_FileSystemWatcher.IncludeSubdirectories = True m_FileSystemWatcher.EnableRaisingEvents = True End Sub 'Extract Application Name Private Function ExtractApplicationName(FullPath as String, RootFolder as String) as string Dim nIndex as Integer' Dim TempStr as String TempStr = FullPath.Substring(len(sPath)+1,len(FullPath)-len(sPath)-1) nIndex = TempStr.IndexOf("\") if nIndex = -1 then TempStr = "Root_Folder" return TempStr else TempStr = TempStr.Substring(0,nIndex) return TempStr end if End Function 'Find whether the log file was finished by const LogFinished as string = "End time --->" Private Function LogFin(FileName as string) as boolean const LogFinished as string = "End time --->" Dim testTxt As StreamReader = New StreamReader(FileName) Dim allRead As String = testTxt.ReadToEnd()'Reads the whole text file to the end testTxt.Close()'Closes the text file after it is fully read. If Regex.IsMatch(allRead, LogFinished) Then 'If the match is found in allRead 'If allRead.IndexIf(LogFinished) <> -1 then Return True Else Return False End If End Function 'Define the event handlers. Private Sub OnChanged(source As Object, e As FileSystemEventArgs) 'if LogFin(e.FullPath) Then 'do not check it here because it is causing an exception at reading a file Application = ExtractApplicationName (e.FullPath, sPath) ProcessFile(e.FullPath, Application) 'end if End Sub 'Private Sub OnRenamed(source As Object, e As RenamedEventArgs) ' ProcessFile(e.FullPath) 'End Sub ' Process a file. Private Sub ProcessFile(ByVal file_name As String, ByVal Appl as String) Dim NewCopy As String Dim Format as String = "yyyyMMdHHmmss" NewCopy = WatcherLogDir & "\" & AppsetName & "_" & Appl & "_" & Now.ToString(Format) & ".log" File.Copy(file_name, NewCopy, true) if LogFin(NewCopy) Then WriteToEventLog (Now.ToString() & ": " & NewCopy & " for appset: " & AppsetName, Watcher_Name) Else File.Delete(NewCopy) End If End Sub 'dispose objects public sub free() m_FileSystemWatcher.Dispose() end sub Public Function WriteToEventLog(ByVal Entry As String, _ Optional ByVal AppName As String = "VB.NET Application", _ Optional ByVal EventType As _ EventLogEntryType = EventLogEntryType.Information, _ Optional ByVal LogName As String = "Application") As Boolean Dim objEventLog As New EventLog() Try If Not objEventLog.SourceExists(AppName) Then objEventLog.CreateEventSource(AppName, LogName) End If objEventLog.Source = AppName objEventLog.WriteEntry(Entry, EventType) Return True Catch Ex As Exception Return False End Try End Function End Class |
LogWatcherService.vb |
---|
Imports System Imports System.ServiceProcess Imports System.ComponentModel Imports System.Configuration.Install Public Class Service1 Inherits System.ServiceProcess.ServiceBase Private watcher as CL_Watcher #Region " Component Designer generated code " Public Sub New() MyBase.New() ' This call is required by the Component Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call End Sub 'UserService overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' The main entry point for the process <MTAThread()> _ Shared Sub Main() Dim ServicesToRun() As System.ServiceProcess.ServiceBase ' More than one NT Service may run within the same process. To add ' another service to this process, change the following line to ' create a second service object. For example, ' ' ServicesToRun = New System.ServiceProcess.ServiceBase () {New Service1, New MySecondUserService} ' ServicesToRun = New System.ServiceProcess.ServiceBase() {New Service1} System.ServiceProcess.ServiceBase.Run(ServicesToRun) End Sub 'Required by the Component Designer Private components As System.ComponentModel.IContainer ' NOTE: The following procedure is required by the Component Designer ' It can be modified using the Component Designer. ' Do not modify it using the code editor. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() components = New System.ComponentModel.Container() Me.ServiceName = "Service1" End Sub #End Region Protected Overrides Sub OnStart(ByVal args() As String) ' Add code here to start your service. This method should set things ' in motion so your service can do its work. dim sPath as String dim nIndex as Integer dim Appset_Name as string sPath = Trim(System.Reflection.Assembly.GetExecutingAssembly().Location) nIndex = sPath.LastIndexOf("\") sPath = sPath.Substring(0, nIndex) nIndex = sPath.LastIndexOf("\") nIndex += 1 Appset_Name = sPath.Substring(nIndex,len(sPath)-nIndex) watcher = new CL_Watcher watcher.Define_Watcher(sPath, Appset_Name) End Sub Protected Overrides Sub OnStop() ' Add code here to perform any tear-down necessary to stop your service. watcher.free() watcher = nothing End Sub End Class <RunInstallerAttribute(True)> _ Public Class ProjectInstaller Inherits System.Configuration.Install.Installer Public WithEvents ServiceProcessInstaller1 As ServiceProcessInstaller Public WithEvents ServiceInstaller1 As ServiceInstaller Public Sub New() MyBase.New() Me.ServiceProcessInstaller1 = New ServiceProcessInstaller() Me.ServiceInstaller1 = New ServiceInstaller() 'ServiceProcessInstaller1 Me.ServiceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem Me.ServiceProcessInstaller1.Password = Nothing Me.ServiceProcessInstaller1.Username = Nothing dim sPath as String dim nIndex as Integer dim Appset_Name as string sPath = Trim(System.Reflection.Assembly.GetExecutingAssembly().Location) nIndex = sPath.LastIndexOf("\") Appset_Name = sPath.Substring(0, nIndex) nIndex = Appset_Name.LastIndexOf("\") nIndex += 1 Appset_Name = Appset_Name.Substring(nIndex,len(Appset_Name)-nIndex) 'ServiceInstaller1 Me.ServiceInstaller1.Description = "Log watcher service for " & Appset_Name Me.ServiceInstaller1.DisplayName = "LogWatcher_" & Appset_Name Me.ServiceInstaller1.ServiceName = "LogWatcher_" & Appset_Name Me.ServiceInstaller1.StartType = ServiceStartMode.Manual 'ProjectInstaller Me.Installers.Add(Me.ServiceProcessInstaller1) Me.Installers.Add(Me.ServiceInstaller1) End Sub End Class |
UninstallLogWatcher.bat |
---|
Path=%path%;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\;C:\Windows\Microsoft.NET\Framework\v3.5;c:\windows\microsoft.net\framework\v4.0.30319; installutil /u D:\PC_MS\Data\Webfolders\ApShell\LogWatcherService.exe PAUSE |