Skip to Content
SAP Business Planning and Consolidation, version for the Microsoft platform

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.

  1. The service is to be located in Appset folder (\PC_MS\Data\Webfolders\[APPSET]\)
  2. Go to the folder \PC_MS\Data\Webfolders\[APPSET]\ (in my example this will be D:\PC_MS\Data\Webfolders\APSHELL_COPY\)



  3. Create the following files in Notepad

    CompileLogWatcher.bat
    InstallLogWatcher.bat
    LogWatcherLib.vb
    LogWatcherService.vb
    UninstallLogWatcher.bat

  4. Copy and paste the code at the bottom of this document into the corresponding files.
  5. Adjust InstallLogWatcher.bat and UninstallLogWatcher.bat with full directory address to the folder \PC_MS\Data\Webfolders\[APPSET]\



  6. Run CompileLogWatcher.bat




  7. Two new files will appear in the folder


  8. Run as administrator InstallLogWatcher.bat




  9. The service is created.
  10. Start – Run and type in Service.msc
  11. Find the service by the name “LogWatcher_[Appset]”
  12. Start the service.




The service was created and started.

Test LogWatcherService

As a prerequisite there should be valid default logic in appset/application

  1. Log on to BPC Excel.
  2. Create new or open an existing input schedule.
  3. Post some data.



  4. Go to \PC_MS\Data\Webfolders\Logs and look for a new file with the following name [APPSET]_[APPL]_[timestamp].log.





Uninstall LogWatcherService

  1. 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