Wednesday, August 30, 2017

WFA Command - advanced logging

I created a powershell module once (somewhere in this blog) that allows you to do some powerfull logging.  I always planned on showing you how it's done as a WFA command.

For my NetApp Insight sessions, I had to create such a command, and I'm already posting it here.

Logging on 3 levels :
- Database
- Logfile
- Eventviewer

How to use :

- Type : debug, info, warn, error or fatal
- Message : the actual logging message
- Cr : A Change Request, I thought it might come in handy, you could leave it out if you want
- LogToFile : boolean, if you want a logfile too
- LogToEventViewer : boolean, if you want to log to the eventviewer
- EventId : in case of eventviewer, you can set the eventid
- Category : in case of eventviewer, you can set the category (leave default, unless you know about this)


- $interactive : set to true if you want to test this in powershell first (run as admin !!)

don't forget to set you shell in WFA debug mode.


now set to "log", so I assumed you have created the scheme "log" and dictionary "log".

$MySqlDatabase : use this to set change the scheme

The dictionary

- log
     - timestamp (datetime)
     - cr (string)
     - message (string 8000)
     - type (enum : debug,info,warn,error,fatal)
     - workflowname
     - jobid (integer)
     - user (string)

The code :

param (

    [parameter(Mandatory=$true, HelpMessage="Type")]

    [parameter(Mandatory=$true, HelpMessage="Message")]

    [parameter(Mandatory=$true, HelpMessage="Change Request")]

    [parameter(Mandatory=$false, HelpMessage="Log to local logfile ?")]

    [parameter(Mandatory=$false, HelpMessage="Log to event viewer ?")]
    [parameter(Mandatory=$false, HelpMessage="Event viewer - Event id")]
    [parameter(Mandatory=$false, HelpMessage="Event view - Category")]

# Default credentials for playground database
$MySQLAdminUserName = 'wfa'
$MySQLAdminPassword = 'Wfa123'
$MySQLDatabase = 'log'
$MySQLHost = "localhost"

# fix single quotes
$SqlMessage = $Message -replace "'","''"

# log message format
$Message = "[$User][$Cr][$WorkflowName][$JobId] - $Message" 

[string]$mySqlDateTime = get-date -Format "yyyy-MM-dd HH:mm:ss"

$interactive = $false     # set this if your are running locally in the shell

# ================================================================
# ==   Get Environment variables (script location, host & port)
# ==   Note : set the datasource port to 1, to trigger debug mode
# ================================================================
    Set-Variable -Name SCRIPT_PATH -Value (Split-Path (Resolve-Path $myInvocation.MyCommand.Path)) -Scope local
    $User = $(Get-WfaRestParameter "userName")
    $Workflowname = $(Get-WfaRestParameter "workflowName")
    $Jobid = $(Get-WfaRestParameter "jobId")
    # auto load wfa profile
    $profilecheck = Get-Item function: | ?{$_.Name -eq "Connect-WfaCluster"}
    if(-not $profilecheck){
        Write-Host "Loading WFA profile..." -ForegroundColor Yellow
        cd 'C:\Program Files\NetApp\WFA\PoSH\'
        . '.\profile.ps1'
    Set-Variable -Name SCRIPT_PATH -Value 'C:\Program Files\NetApp\WFA\jboss\standalone\tmp\wfa' -Scope local
    cd "$SRIPT_PATH"
    $User = "dummy_user"
    $Workflowname = "dummy_workflow_name"
    $Jobid = "123"

# store to db
Get-WFALogger -Info -message $("Preparing Command")
$cmd1 = "INSERT INTO $MySQLDatabase.log SET " + 
    "Message='$SqlMessage'," +
    "Timestamp='$mySqlDateTime'," +
Get-WFALogger -Info -message $("Executing SQL: " + $cmd1)
Invoke-MySqlQuery -query $cmd1

# Event Viewer Function
function SetLog4NetEventLogEventId{
        [int] $EventId,
        [int] $Category
    [log4net.ThreadContext]::Properties.Item("EventID") = $EventId
    [log4net.ThreadContext]::Properties.Item("Category") = $Category

if($LogToFile -or $LogToEventViewer){
 $LoggerName = $MySQLDatabase
 $dllLocation = $SCRIPT_PATH + "\..\..\..\..\Posh\Modules\DataOntap\log4net.dll"
 $logfile = $SCRIPT_PATH + "\..\..\log.war\jboss\" + $LoggerName + ".logger.log"

 # Initialize log4net
 [Reflection.Assembly]::LoadFrom($dllLocation) | Out-Null

 #Reset the log4net configuration

 #Define new logger for this module only
  Get-WfaLogger -warn -message "[LOG] Repository already created"
 [log4net.Repository.Hierarchy.Hierarchy]$repository = [log4net.LogManager]::GetRepository("$($LoggerName)Logger")

 # create new appenders

  # create rolling file logging
  Get-WfaLogger -info -message "[LOG] LogFile path is : '$logFile'"
      New-Item -Path $logFile -type file -EA Stop
            if($_.Exception -match "denied"){
                Get-WfaLogger -warn -message "[LOG] No access to write log file"
                Get-WfaLogger -warn -message "[LOG] $($_.Exception)"
  $logPattern="%d %w %-5p %c : %m%n"
  $rollingLogAppender = new-object log4net.Appender.RollingFileAppender
  $rollingLogAppender.MaximumFileSize = "50MB"
  $rollingLogAppender.Name = "file"
  $rollingLogAppender.File = $logFile
  $rollingLogAppender.RollingStyle = "Size"
  $rollingLogAppender.StaticLogFileName = $true
  $rollingLogAppender.MaxSizeRollBackups = "20"
  $rollingLogAppender.Layout = new-object log4net.Layout.PatternLayout($logPattern)
  $rollingLogAppender.Threshold = [log4net.Core.Level]::Debug
  Get-WfaLogger -info -message "[LOG] File Logger initialized"

  # create eventlog logging
  $eventAppender = new-object log4net.Appender.EventLogAppender
  $eventAppender.Name = "event"
  $eventAppender.ApplicationName = "$($LoggerName)"
  $eventAppender.EventId = 1
  $eventAppender.Layout = new-object log4net.Layout.PatternLayout($eventPattern)
  $eventAppender.Threshold = [log4net.Core.Level]::Debug
  Get-WfaLogger -info -message "[LOG] Event viewer Logger initialized"
 # mark as configured
 $repository.Configured = $true
    Get-WfaLogger -info -message "[LOG] Logger initialized"

        SetLog4NetEventLogEventId -EventId $EventId -Category $Category
            Get-WfaLogger -info -message "[LOG][$Type] - $Message"
            Get-WfaLogger -info -message "[LOG][$Type] - $Message"
            Get-WfaLogger -info -message "[LOG][$Type] - $Message"
            Get-WfaLogger -info -message "[LOG][$Type] - $Message"
            Get-WfaLogger -info -message "[LOG][$Type] - $Message"
Download dar

1 comment :

  1. I'm trying to debug a problem with a PowerShell-based Command... When I run the PS script outside of WFA (but on the same host), the script works. When I run the PS Command as part of a WorkFlow, it fails with the following error:

    12:30:13.268 WARN [IBM Remove vdisk FC Mappings] ***DEBUG ***: fcMapInfo contains: id,4 name,fcmap4 source_vdisk_id,66 source_vdisk_name,RSTRATTON_0066 target_vdisk_id,73 target_vdisk_name,RSTRATTON_0066_01 group_id, group_name, status,idle_or_copied progress,0 copy_rate,50 start_time, dependent_mappings,0 autodelete,off clean_progress,100 clean_rate,50 incremental,on difference,100 grain_size,256 IO_group_id,0 IO_group_name,io_grp0 partner_FC_id, partner_FC_name, restoring,no rc_controlled,no keep_target,no type,generic restore_progress,0 fc_controlled,no
    12:30:13.268 WARN [IBM Remove vdisk FC Mappings] Currently working on: id,4
    12:30:13.315 ERROR [IBM Remove vdisk FC Mappings] Cannot bind argument to parameter 'Value' because it is an empty string.

    The part of the script it is failing on is a part that takes an array that contains xxx,yyy pairs, and splits them out and uses xxx as the FieldName and yyy as the FieldValue.

    I've modified the script to account for entries that may be blank for yyy (for example "start_time, " ... I have my script modify that item to be "start_time,EMPTY".

    Again, the script works fine when run outside WFA, on the same host that WFA is installed on. Any ideas or suggestions?