Log anything with JSON

ludodepaz
Contributor

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

ludodepaz_0-1697041734901.png

Second, when you want to log, use JSON!

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

ludodepaz_2-1697042316707.png

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!

15 REPLIES 15

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.

Hi,

I just posted something on that topic:

[How to] Log into a file instead of the Error Log - OneStream Community (onestreamsoftware.com)

I hope it will help!

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!

DanielWillis
Contributor III

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

ludodepaz
Contributor

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

cjohnson_0-1697643850977.png

 

 

		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
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 - LogUsingJson_Results.jpg
Many thanks for your help
Wei

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

Oh I see, the Local Description field is misleading. Only the Default Description is stored in the MemberInfo table
Descriptions in multiple languages are stored in another table, you can read it in a rule with a Dictionary(Of String, Memberdescription).

This is a good place to start: Solved: Cannot save Descriptions to a member - OneStream Community (onestreamsoftware.com)

Wei
New Contributor III

Hi Ludo, I don't have access to the link you shared, are you able to provide more details on that? I will give Dictionary a try.

Thanks

Wei

Wei
New Contributor III

Sorry Ludo - the same "Access Denied"!