newrelic-dotnet-agent icon indicating copy to clipboard operation
newrelic-dotnet-agent copied to clipboard

Instrumenting PowerShell 7 Scripts, Modules, and Functions

Open Vacant0mens opened this issue 3 years ago • 5 comments

I have a script that runs as a Windows Service that has two functions. One function is called in a loop that runs every five minutes, and that function calls the second function (in mine it's multiple times with lots of logic, but for brevity, I've only put it in once). Shown below.

I assumed that since PowerShell is based on .NetCore/.Net 6 that it would just be picked up automatically. But sadly this is not the case. I would love to see metrics (or anything really) about my script and what it's doing in APM, and potentially alert off of it.

My script has no problem importing the NewRelic.Api.Agent.dll and doesn't throw any errors when I add the [NewRelic.Api.Agent.Transaction()] and the [NewRelic.Api.Agent.Trace()] attributes (it even throws errors when I change the names slightly, so I have the names right at least), but NR APM doesn't pick up any data.

$AgentDLL = ".\lib\netstandard2.0\NewRelic.Api.Agent.dll"
Add-Type -Path $AgentDLL

function Invoke-MyTracedFunction {
    [NewRelic.Api.Agent.Trace()]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Parameter1
    )
    # do some stuff
}

function Invoke-MyMainFunction {
    [NewRelic.Api.Agent.Transaction()]
    [CmdletBinding()]
    param ()
    Invoke-MyTracedFunction -Parameter1 "some data"
}
while ($true) {
    Invoke-MyMainFunction
    Start-Sleep 300
}

I'm curious, why doesn't it get any data? Also, is there a way to add PowerShell to the .NetCore/.Net 6 Agent compatibility?

As a DevOps Engineer, it would be very useful to see metrics and such about my scripts and modules that I run, especially ones that are run automatically in one way or another.

Vacant0mens avatar Jul 07 '22 22:07 Vacant0mens

Hey @Vacant0mens, thanks for the feature request! This idea intrigued me so I tried it out with our profiler attached. Unfortunately even though powershell appears to let you add our attributes, actual .NET functions with the names Invoke-MyMainFunction or Invoke-MyTracedFunction are never encountered by the profiler.

I tried invoking a web request via powershell and our existing instrumentation did rejit the appropriate .net function, so I have hope there may be some way to accomplish this.

My guess is that if you loaded a proper .NET assembly that contained functions with the Transaction attribute, and called them, the .NET agent would report that while attached to your powershell script. I realize this defeats your use case though.

I've assigned this to our product owner, and we will let you know what is decided.

JcolemanNR avatar Jul 08 '22 14:07 JcolemanNR

Hi @JcolemanNR Just so I'm clear, in your tests, did you use the .Net libraries to make your web request? or did you use a cmdlet?

Also, thanks for submitting the feature request! It'd be pretty great to be able to monitor long-running or always-running/scheduled scripts. 👍

Vacant0mens avatar Aug 01 '22 20:08 Vacant0mens

Hey @Vacant0mens,

I was using Invoke-WebRequest and this was calling a lower level C# api that we do instrument (but only in a transaction).

Here is my test script that did not work. I think it's basically your script with an additional line or two:

$AgentDLL = "C:\src\newrelic-dotnet-agent\src\Agent\newrelichome_x64_coreclr\NewRelic.Api.Agent.dll"
Add-Type -Path $AgentDLL

function Invoke-MyTracedFunction {
    [CmdletBinding()]
    [NewRelic.Api.Agent.TraceAttribute()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Parameter1
    )

    # do some stuff
    Invoke-WebRequest -URI https://www.bing.com/search?q=how+many+feet+in+a+mile
}

function Invoke-MyMainFunction {
    [CmdletBinding()]
    [NewRelic.Api.Agent.TransactionAttribute()]
    param ()
    Invoke-MyTracedFunction -Parameter1 "some data"
}

while ($true) {
    Write-Output "Invoking function"
    Invoke-MyMainFunction
    Start-Sleep 5
}

The core issue with the above example is that the Cmdlet functions are never encountered by the Microsoft Profiler API for JIT/Rejit, so the .NET Agent is unable to inject the required code to observe the lifecycle of these functions. The [Trace] and [Transaction] attributes are observed during this Jit/Rejit process, but since this doesn't happen, no transaction is ever started.

Based on my experimentation, I don't think instrumenting cmdlets as a [Transaction] will work with the current .NET agent. The only thing I could see working would be calling out to a custom assembly that contains business logic, and wraps the entry point with a [Transaction] attribute. This would likely defeat the benefit of scripting in PowerShell though.

JcolemanNR avatar Aug 01 '22 21:08 JcolemanNR

Could that (in theory) be a new/different NewRelic.Api.Agent.DLL made for PowerShell specifically? If not, what is the feature request is for? (Just making sure I understand)

Vacant0mens avatar Aug 02 '22 02:08 Vacant0mens

Hey @Vacant0mens, good question! The feature request label was added to this ticket since the agent was not designed with this use case in mind, and it's not a trivial change to make it work as foundational things are not working (like JIT/ReJIT notifications for powershell cmdlets). There are a few ideas out there for alternative APIs that the agent API dll could support to enable the powershell use case, and they all fall in to the new development (feature request! 😄) bucket versus resolving a bug/issue.

The best approach, so far, to implement support for powershell would be to extend the Agent API to allow transactions/segments to be manually started/ended within your script by decorating your functions/segments of interest. There is also some potential crossover with the concept of transaction-less data (similar to open telemetry) that could be useful for the scripting use case.

Those are the answers from a technical perspective. I'd have to defer to product management for any questions about priority/timeframe for implementing such a feature though.

JcolemanNR avatar Aug 02 '22 23:08 JcolemanNR

https://issues.newrelic.com/browse/NEWRELIC-3651

This issue won't be actioned.

@angelatan2 does that last comment by workato-integration bot mean that it's done or that it won't get worked on ever?

Vacant0mens avatar Oct 06 '22 16:10 Vacant0mens

@Vacant0mens, please pardon the automated message. Typically, this means that Product Management has reviewed the feature request and also reviewed the demand for the feature. At this time, he has decided that it is not part of our annual roadmap. But, of course, he continues to monitor requests from the community and our customers and frequently revisits the feature requests as interests changes.

angelatan2 avatar Oct 07 '22 21:10 angelatan2