How do you guys handle logging?

Since endpoints can run multiple instances I’m running into an issue where 2 threads are trying to write to the same file at the same time causing an error due to the file being locked.

I thought about instead having each transaction logged to its own file then having a single master thread merge the transaction logs to a main file. However, that seems kinda clunky to me.

Just wondering if anyone else has gone through this before.

I’m using SQLite and writing action/log events to that.
I created a simple database with the app DB.Browser.for.SQLite-3.12.1-win32 then installed the PowerShell module Install-Module PSSQLite which you can then insert into the d/b with something like:

$query = “INSERT INTO $cache:cfgSQLiteTableUserUsage (TimeStamp,UserName,Source,Action,CommandIssued,IPAddress,CfgEnvironment,Server) VALUES (@TimeStamp,”"$User"",""$Source"",""$Action"",""$CommandIssued"",@IPAddress,@CfgEnvironment,@Server)"

        Invoke-SqliteQuery -DataSource $cache:cfgSQLiteDatasource -Query $query -SqlParameters @{
            TimeStamp = Get-Date -Format "yyyy/MM/dd HH:mm:ss.fff"
            IPAddress = $RemoteIpAddress
            CfgEnvironment = $cache:cfgEnvironment
            Server = $env:computername
        }

Then I have a page in the dashboard that shows a table, pulling the data from the SQLite table so the dashaboard/website admins can view logs/actions/usage from within the dashboard.

I personally am a fan of the real-time viewing capabilities of CMTrace (standalone executable, part of SCCM tools):

https://www.microsoft.com/en-us/download/confirmation.aspx?id=50012

I wrote a function to write out to a log file, which I can then open with CMTrace. It marks errors and warnings for you, and you can define different sections to help organize the logging even within a single script.

function _writeLog {
   [CmdletBinding()]
      Param(
         [parameter(Mandatory=$true)]
            [String]$Path,

         [parameter(Mandatory=$true)]
            [String]$Message,

         [parameter(Mandatory=$true)]
            [String]$Component,

         [parameter(Mandatory=$true)]
            [ValidateSet("Info", "Warning", "Error")]
               [String]$Type
      )

   switch ($Type) {
      "Info" { [int]$Type = 1 }
      "Warning" { [int]$Type = 2 }
      "Error" { [int]$Type = 3 }
   }

   # Create a log entry
      $Content = "<![LOG[$Message]LOG]!>" +`
         "<time=`"$(Get-Date -Format "HH:mm:ss.ffffff")`" " +`
         "date=`"$(Get-Date -Format "M-d-yyyy")`" " +`
         "component=`"$Component`" " +`
         "context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " +`
         "type=`"$Type`" " +`
         "thread=`"$([Threading.Thread]::CurrentThread.ManagedThreadId)`" " +`
         "file=`"`">"

    $boolWritten = $false

    foreach ($a in 1..10) {
       if ($boolWritten -eq $false) {
          try {
             Add-Content -Path $Path -Value $Content -ErrorAction Stop
             $boolWritten = $true
             break
          } catch {
             Start-Sleep -Milliseconds 250
         }
     }
}       

}

Example1: Information written to a log file after successfully creating a user in Active Directory

_writeLog -Path $LogFile -Message “User $($user.EmployeeID) created successfully in $($user.Company) domain” -Component “New User” -Type Info

Example2: Critical error written to a log file after failure to delete user from A.D.

_writeLog -Path $LogFile -Message “Failed to remove User $($user.EmployeeID) from $($user.Company) domain. Error is $($Error[0].Exception)” -Component “New User” -Type Error

3 Likes