Forum Discussion

ludodepaz's avatar
ludodepaz
Contributor
2 years ago

Log anything with JSON

I showed it at Wave and share it with all of you: when you use JSON to log, you can log anything, not just strings, you can log a Dictionary, a list, a MemberInfo, even a Databuffer.

First, you need to import JSON, add these 2 rows at the top of your rule:

Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq

Second, when you want to log, use JSON!

Dim stringobj As String = JsonConvert.SerializeObject(lCenterInfo,Formatting.Indented)
api.logmessage(lCenterInfo.Member.Name & ", lCenterInfo: " & stringobj)

When you use ,Formatting.Indented, your log will be correctly formatted.

Thanks to Matt Ha for enlightening me with JSON and RobbSalzmann for sharing how to have the JSON string correctly formatted!

  • DanielWillis's avatar
    DanielWillis
    Valued Contributor

    I think its probably worth mentioning that you can do all of this without JSON still (not saying there aren't reasons to use JSON) and perhaps in a more human-readable format.

    For example, here is a bit of code outputting some datatable details. I don't think I wrote this bit of code I just had it saved in my code repository but I do this sort of thing all the time:

     

     

                    api.logmessage(
                        si,
                        "Datatable output",
                        String.Join(
                            vbnewline,
                            dtSimple.AsEnumerable.Select(Function(x) String.Concat(
                                "Source:",x.Item("Company code"),vbnewline,
                                "Target",":",x.Item("Fiscal year"),vbnewline,
                                "Product",":",x.Item("GL Account"),vbnewline
                            ))))

     

    I didn't get to go to your presentation ludodepaz  (due to everything being on at once!) but I wanted to and did go through the slides. I thought the coolest concept was where you were logging in memory and outputting to log/disk only when required.

     

     

    .

  • ChristianW's avatar
    ChristianW
    Valued Contributor

    Thank you, I love it.

    I combined it with the recommendations from the Advanced Solution Engineering Concepts session about the use of extensions.

    With this addition:

    Imports System.Runtime.CompilerServices
    
    Imports Newtonsoft.Json
    
    ...
    
    Public Module LogHelper
    	<Extension()>
            Public Sub LogObject(ByVal errorLogApi As IBRApiErrorLog, ByVal si As SessionInfo, ByVal message As String, objectToLog As Object)
                Try
    				
    				Dim objAsJsonString As String = JsonConvert.SerializeObject(objectToLog, Formatting.indented)
    				
    				brapi.ErrorLog.LogMessage(si, message, objAsJsonString)
                   
                Catch ex As Exception
                    Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    			End Try
            End Sub
    
    End Module

    You can call the logging like this

    brapi.ErrorLog.LogObject(si, "Log Object", someObject)

    It works really nicely, cheers

  • bsalamone's avatar
    bsalamone
    New Contributor II

    Hi Ludo,

    Thanks for sharing these screenshots. I'm trying to get the JSON logging to work in our environment and was wondering if you could also share a full screenshot of the 2 public functions? The presentation files have some text running over them so that I can't see the full syntax for those. Thanks for any help.

  • cjohnson's avatar
    cjohnson
    New Contributor III

    I think the conversion of the powerpoint to pdf may have stripped some of the images in the Wave documentation. Only the code for the WriteLogger Function is present. The AppendDBToLogger function seems to be missing. Could you share the code for it?

    Thanks in advance!

  • That's very nice!

    I need to find a good way to share the rules to log into a file and I get back to you.

    • cjohnson's avatar
      cjohnson
      New Contributor III

      Here is the image for the WriteLogger function from the pdf without the overlaying image, along with the code (or at least my best attempt to transcribe it, haha). 

       

       

      		Public Shared Function WriteLogger (ByVal si As SessionInfo, ByVal api As Object, ByRef logger As System.Text.StringBuilder, 
      											Optional ByVal fileName As String = "LOG", Optional ByVal folderName As String = "TestLogs", 
      											Optional ByVal filesuffix As String = Nothing) As String
      		'Saves log to text file on application database
      		'
      		'Parameters
      		'	logger: string builder object with contents of log file
      		'	fileName: optional log file name. defaults to LOG
      		' 	folderName: optional log folder destination. defaults to TestLogs
      		'	fileSuffix: optional log file suffix. defaults to system time
      		'Returns
      		'	filepath of log file
      		
      		If filesuffix Is Nothing Then
      				fileSuffix = DateTime.Now.ToString("yyyy-MM-dd_HHmmss")
      			End If
      			
      			Dim logFileName As String = fileName & "_" & filesuffix & ".txt"
      			Dim targetFolder As String = folderName
      			Dim targetPath As String = "Documents/Public"
      			
      			'Create folder if it does not exist
      			Dim folderPath As XFFolderEx = BRApi.FileSystem.GetFolder(si, FileSystemLocation.ApplicationDatabase, targetPath & "/" & targetFolder)
      			If folderPath Is Nothing Then
      				BRapi.FileSystem.CreateFullFolderPathIfNecessary(si, FileSystemLocation.ApplicationDatabase, targetPath, targetFolder)
      			End If
      			
      			'Create File with logger contents and save in folder
      			Dim targetDir As String = BRApi.FileSystem.GetFolder(si, FileSystemLocation.ApplicationDatabase, targetPath & "/" & targetFolder).XFFolder.FullName
      			Dim dbFileInfo As New XFFileInfo(FileSystemLocation.ApplicationDatabase, logFileName, targetDir, XFFileType.Unknown)
      			dbFileInfo.ContentFileContainsData = True
      			dbFileInfo.ContentFileExtension = dbFileInfo.Extension
      			Dim dbFile As New XFFile(dbfileInfo, String.Empty, System.Text.Encoding.UTF8.GetBytes(logger.ToString))
      			BRApi.FileSystem.InsertOrUpdateFile(si, dbfile)
      			
      			Return dbFile.FileInfo.FullName
      		End Function

       

  • Wei's avatar
    Wei
    New Contributor III

    Hi Ludo, 

    Many thanks for sharing the code for logging using Json - I tried but I am not getting any results. Here is my code:

    Imports Newtonsoft.Json
    Imports Newtonsoft.Json.Linq
     
    Namespace OneStream.BusinessRule.Finance.FinVB
    Public Class MainClass
    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As FinanceRulesApi, ByVal args As FinanceRulesArgs) As Object
    Try
    Select Case api.FunctionType
     
     
    Case Is = FinanceFunctionType.CustomCalculate
    If args.CustomCalculateArgs.FunctionName.XFEqualsIgnoreCase("TestJsonLog") Then
    Dim objDimPk As DimPk = api.Dimensions.GetDim("UD1Main").DimPk
    Dim ctryList As List(Of MemberInfo) = api.Members.GetMembersUsingFilter(objDimPk, "U1#APAC.Base", Nothing)
    Dim ctryInfo As MemberInfo
    For Each ctryInfo In ctryList
    If Not ctryInfo Is Nothing Then
    Dim stringObj As String = JsonConvert.SerializeObject(ctryInfo, Formatting.Indented)
    BRApi.ErrorLog.LogMessage(si, ctryInfo.Member.Name & ", ctryInfo: " & stringObj)
    End If
    Next
     
    End If
    End Select
     
    Return Nothing
    Catch ex As Exception
    Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    End Try
    End Function
    End Class
    End Namespace
     
    And, here are my result - not sure what I am missing - 
    Many thanks for your help
    Wei
    • ludodepaz's avatar
      ludodepaz
      Contributor

      Hi Wei,

      the results look good to me. You only see 1 member at a time, you can log ctryList  =and you will see the entire list if it's what you're looking for.

      best

      • Wei's avatar
        Wei
        New Contributor III

        Thanks Ludo - I am getting "null" for LocalDescription for all of them, where your deck shows there should be some property information listed under "LocalDescription" - are there any other config I need to do to see them?

        Thank you

        Wei