Writing CMTrace format Log files with Powershell

Anyone who has administered a little SCCM in their time, would be be familiar with the tool CMTrace or SMSTrace. CMTrace is a tool that comes with System Center Configuration Manager. It allows you to view the myriad of logs files that SCCM generates in a consistent and easy to read format. Being used to the log format that CMTrace generates, I thought it would be a great idea to use that format for logging Powershell script actions and errors.

I hunted around the web and found a few versions of a log function using CMTRace. The best implementation that I found was from Russ Slaten at the System Center Blog site. It works really well but I wanted to add some different functionality.

CMTrace.exe is part of the System Center 2012 R2 Configuration Manager Toolkit, which I believe anyone can download. If you have System Center Configuration Manager installed in your environment, you can also grab it from the \\”SiteServer”\Program Files\Microsoft Configuration Manager\tools\cmtrace.exe.

There are a few different use cases for when logging is handy: –

  • Running Powershell scripts as Scheduled Tasks.
  • Auditing automation centrally in a team environment.
  • The output of a log could be used as part of change management processes.
  • Debugging locally and remotely

I’ll start off by posting the script. Below I’ll go into some information that might help in using the script.

**NOTE: This has only been tested on Powershell v5.0. The information stream is a new feature of Powershell v5.0.**

Here is the script: –

I’ve included a help file inline to help with calling the function.

Where does the log file get created?

The function by default will log to $Env:temp\powershell-cmtrace.log. You can however pass a file location to the function if you would like to the log file in a different location.

2016-01-03_18-40-34

What does the output look like on the host?

Here is an example.

2016-01-03_18-47-36

Why does Error only log one line back to the host, where is all the ErrorRecord informaiton?

I wanted to write back to the host in a consistent fashion. The error record is still there I’m just not passing it back to the host. The full error record gets put in the log file and you can view that information by clicking on the Error entry and looking at the window down the bottom of CMTrace. Also, you can still access the error information in the host by using the $Error variable. If it were the last error you would access it by using the first index of the $Error variable. eg. $Error[0].

2016-01-06_7-49-06

What does the output to the log file look like?

It looks like this. Notice the Error information is in the box down the bottom.

2016-01-03_18-51-25

I don’t want to see the output on the host, I just want it to log in the CMTrace format.

There is a parameter switch called WriteBackToHost. By default it’s set to True, but if you don’t want to see the output then set this to false.

How do I call this function?

To call this function, either dot source it, run it from ISE and then call it, put it in your profile or pop it in a module and Bob is your uncle.

Gimme some examples of how to use the Write-CMTracelog function.

Here are some examples of how you would use this advanced function that are in the help section of the function.

Examples:

The below example shows how to output a verbose message.

This example shows how to use the Preference variables with the Write-CMTracelog function. It should obey any preference variables that are set in the same scope from where the function has been called.

This example shows how to use the function with a terminating error by using the $Error variable in the message parameter.

 

Hope this function helps you out. Feel free to use and modify to suit your needs.

Sources:

http://blogs.msdn.com/b/rslaten/archive/2014/07/28/logging-in-cmtrace-format-from-powershell.aspx
https://www.microsoft.com/en-us/download/details.aspx?id=50012
http://blogs.technet.com/b/heyscriptingguy/archive/2015/07/04/weekend-scripter-welcome-to-the-powershell-information-stream.aspx

Tagged , , . Bookmark the permalink.

6 Responses to Writing CMTrace format Log files with Powershell

  1. Terry Wallner says:

    Works as Advertised! Thank you!

  2. Scott says:

    Thanks for the code. I’m running PS 5 on Win10:
    Name Value
    —- —–
    PSVersion 5.0.10586.494
    PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
    BuildVersion 10.0.10586.494
    CLRVersion 4.0.30319.42000
    WSManStackVersion 3.0
    PSRemotingProtocolVersion 2.3
    SerializationVersion 1.1.0.1

    OOTB, I get duplicate messages for WARNING and ERROR. I can probably track that one down.

    However, I also get an error when I run this code:

    # User defined error
    Write-CMTraceLog -Logfile C:\PowerShell\Scripts\Junk\foo.log -Message Message -Type Error -WriteBackToHost -ErrorAction continue

    # Cmdlet error (your example use case)
    Try{
    Get-Process -Name DoesnotExist -ea continue
    }
    Catch{
    Write-CMTraceLog -Logfile “C:\PowerShell\Scripts\Junk\foo.log” -Message $Error[0] -Type Error
    }

    The property ‘exception’ cannot be found on this object. Verify that the property exists.
    At C:\PowerShell\Functions\Write-CMTraceLog.ps1:173 char:16
    + if($Message.exception.Message){
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

    Get-Process : Cannot find a process with the name “DoesnotExist”. Verify the process name and call the
    cmdlet again.
    At C:\PowerShell\Scripts\Junk\Write-CMTraceLogTest.ps1:9 char:5
    + Get-Process -Name DoesnotExist -ea continue
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (DoesnotExist:String) [Get-Process], ProcessCommandExcepti
    on
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

    The error line is not written to the log file.

    I tried changing the line $Message.exception.Message to $Message -is [System.Exception] but that didn’t fix it.

    • wolffhaven says:

      Hey Scott,

      I’ve updated the script in the article with the latest version that I’m running. That will fix the duplicate issues.

      I ran the following:

      # User defined error
      Write-CMTraceLog -Logfile C:\PowerShell\Scripts\Junk\foo.log -Message Message -Type Error -WriteBackToHost -ErrorAction continue

      And this came back ok for me, i got the expected result. Maybe try the updated function I have posted and see if it fixes your issue with this one.

      I then tried the other example you were having issues with:

      # Cmdlet error (your example use case)
      Try{
      Get-Process -Name DoesnotExist -ea continue
      }
      Catch{
      Write-CMTraceLog -Logfile “C:\PowerShell\Scripts\Junk\foo.log” -Message $Error[0] -Type Error
      }

      And I think i found the issue. When using a Try/Catch block you need a terminating error. I think this is why you need to set the erroraction to stop. If it’s set to continue, the catch block is skipped. Try for yourself by adding a Write-Host line in the catch block.

      try{
      Get-Process -Name Doesnotexist -ea Stop
      }
      catch{
      Write-CMTraceLog -Logfile “C:\temp\foo.log” -Message $Error[0] -Type Error
      Write-host ‘test’
      }

      When you run this, you’ll see that the write-host is skipped.

      If you try running:

      Try{
      Get-Process -Name DoesnotExist -ea stop
      }
      Catch{
      Write-CMTraceLog -Logfile “C:\PowerShell\Scripts\Junk\foo.log” -Message $Error[0] -Type Error
      }

      This should work for you.

      Let me know how you go!

      I have a question for you if you got time…. This function serves a purpose for me when running scripts as scheduled tasks etc, stuff where I won’t be able to view Write-Verbose or see the output to the console to see if everything has run ok. While it works, (most of the time), there’s got to be a better way. Have you stumbled on any other ways to do logging when running powershell scripts? Maybe using Start-Transcript might be a better way but then that’s a bit TOOOOO verbose?

  3. Scott says:

    Hey Wolffhaven,

    Thanks for the quick reply. And apologies for my very late reply. I’ve just been really really busy lately; I’m on a global committee that is taking up a lot of my free time.

    I had never used cmtrace before. After “playing” with it a bit, I thought it was “ok”, but I wished it was extensible with respect to the columns available. For example, I might want to put elapsed time for each step in the cmtrace log. Still, I might continue to use it…not sure.

    Anyway, to make a long story short, I Googled a lot more and came across NLog. Here are a few links:

    http://nlog-project.org/
    https://github.com/NLog/NLog/wiki/Extending-NLog
    http://stackoverflow.com/questions/710863/log4net-vs-nlog (long but good)
    https://www.loggly.com/blog/benchmarking-5-popular-net-logging-libraries/

    https://rafpe.ninja/2015/08/31/powershell-using-nlog-to-create-logs/
    https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Logging-0c8f1c50
    https://thomasswilliams.github.io/2016/04/29/nlog-in-powershell.html

    There’s many more hits, that’s enough for now! Just Google “NLog”, “NLog vs.”, and “NLog Powershell”.

    In my case, I’d written a PS script that used the .Net SqlBulkCopy class to bulk copy SQL Server data from one database to another. I added a lot of nice console output messages, which was great for interactive use. But what about when we go production and want to run the script in batch? Start-Transcript works “ok”(ish), but didn’t have the logging I wanted.

    I don’t know the log format that cmtrace needs…but you do. So, if I were you (i.e. I had your cmtrace knowledge), I would 1) read up on NLog – it looks like a good package to have in your “kit bag”, and 2) write an extension to NLog for cmtrace output. I’m no .Net expert, but the Wiki made it look relatively easy to extend NLog to different output formats.

    Perhaps you could even submit your extension to the NLog project for inclusion in a future release?

    I will certainly read up on NLog when I get more time, and learn to incorporate it into my scripts where I need advanced logging. And if I get the chance to do some .Net programming, it’s cool that what I’ve learned with NLog is transferrable.

    I hope this helps. And thanks again for your reply. If you have further questions feel free to email directly, or obviously share here if you think it’s useful to everyone.

  4. Young Pae says:

    I have been waiting for this script for so long! Thank you so much. One thing though, on PowerShell ISE v4.0, -WriteBackToHost:$true doesn’t write back to the host screen. Is there anything that I can do to fix? Thanks,

Leave a Reply