Need MetricKit Implementation details for MacOS background Application, mainly for

Hi, We are trying to integrate Metric Kit into our MacOS Application. Our application is a background process. We are interested in getting CPU and Memory metrics for our process. MXMetricPayload is the one we are looking at. We tried to integrate metric Kit and left the background app for 24 hours, we did not get any callback. So, does metric kit work for background app in MacOS? Also does it for Network Extension?

Answered by DTS Engineer in 882750022
we received approximately 48 hours of past metric payloads

Oh, interesting. I’m not 100% sure what’s going on here but this behaviour doesn’t surprise me. Internally, MetricKit uses an XPC activity to drive metric delivery [1], and XPC activities a very much a best effort kinda thing.

could u comment if MetricKit works for NE Process?

Well, I think you have pretty solid evidence that it doesn’t (-:

I’m going to talk about what’s going on in a sec, but before I do that I want to give you some concrete advice: If you want MetricKit to work in your NE system extension, I encourage you to file an enhancement request that describes your setup and why MetricKit is important to you. Once you’re done, please post your bug number here, just for the record.

As to why this doesn’t work, consider this:

% find /System/Library/LaunchDaemons /System/Library/LaunchAgents -name "*[^a-z]metrickitd*" 
/System/Library/LaunchAgents/com.apple.metrickitd.plist

The MetricKit ‘daemon’ is not actually a daemon but rather a launchd agent. That means it’s only available to programs running in some sort of user login session [2]. All system extensions run in the global context, and thus don’t have access to services vended by an agent.

For more about execution contexts on macOS, see TN2083 Daemons and Agents. It’s super old, but the fundamentals are still valid.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] You can see evidence for this its launchd property list:

% plutil -p /System/Library/LaunchAgents/com.apple.metrickitd.plist
{
  …
  "Label" => "com.apple.metrickitd"
  "LaunchEvents" => {
    "com.apple.xpc.activity" => {
      "com.apple.metrickitd.setup" => {
        "AllowBattery" => true
        "Delay" => 60
        "Priority" => "Maintenance"
        "Repeating" => false
        "RequireScreenSleep" => true
      }
    }
  }
  …
}

Be aware, however, that this is only a part of the story. The relationship between MetricKit and XPC activities is very much an implementation detail.

[2] The exact set of sessions is set by the LimitLoadToSessionType property in the launchd property list. You can learn more about this in the launchd.plist man page, along with TN2083. I’m not going to explore it here because it’s not relevant to your situation.

Also one more point, when i integrated metric Kit and used Xcode to simulate MetricKit payloads, i was getting the callback i.e func didReceive(_ payloads: [MXMetricPayload])

Main issue was that when i launched my app with changes and waited for more than 24 hours, but i did not get any callback, so wanted to know if it works on background apps or not? Also does it work on MacOS or not? Could u pls help to answer it

MetricKit definitely works on macOS.

I’m not sure about how it interacts with Network Extension processes. Let’s start with some basics:

  • Is your NE provider packaged as an app extension or a system extension?
  • If it’s an sysex, are you distributing it directly? Or on the Mac App Store.

TN3134 Network Extension provider deployment talks more about NE provider packaging and deployment options.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for looking into this. Here is a complete summary of our setup and what we have confirmed so far.

App configuration:

  • macOS app distributed via Developer ID (PKG installer) — not the Mac App Store
  • Host app runs as a menu bar agent (LSUIElement = YES in Info.plist) NSApplicationActivationPolicy = .accessory
  • The app runs continuously as a login item — it is always alive
  • Network Extension is a System Extension (NEAppProxyProvider), always running alongside the host app

What we have implemented:

  • MXMetricManager.shared().addSubscriber(self) is called in applicationDidFinishLaunching in the host app — as early as possible
  • A separate MXMetricManagerSubscriber is registered inside the Network Extension process at tunnel start
  • Both didReceiveMetricPayloads: and didReceiveDiagnosticPayloads: are implemented
  • On startup we also drain pastPayloads and pastDiagnosticPayloads to catch anything queued while the process was not running
  • The subscriber object is kept alive for the full lifetime of both processes

What we have confirmed:

  • Xcode's "Simulate MetricKit Payloads" works correctly in the host app. Our delegates fire, payloads are received. The code is correct.
  • Real production payloads: zero callbacks after 24+ hours in both processes i.e Host app and NE process

Our questions:

  1. Does MetricKit's real 24h payload delivery require App Store or TestFlight distribution? We are using Developer ID — does metrickitd require an App Store receipt to mark an app eligible for delivery?

  2. Does metrickitd deliver payloads to LSUIElement = YES menu bar agents (.accessory activation policy)? Since these apps never become the "active application" in the OS sense, we are concerned the delivery trigger may never fire for our process.

  3. If both of the above are blockers — is there any supported path to get real MetricKit payload delivery for a Developer ID distributed, LSUIElement menu bar app with a System Extension?

Thanks for all the extra info.

Just for the sake of debugging, I want to focus on this:

Both didReceiveMetricPayloads: and didReceiveDiagnosticPayloads: are implemented

When an app crashes, MetricKit delivers the diagnostic playload immediately after the user relaunches the app. This is great way to test whether the basics are working:

  1. Start your app.

  2. Kill it from Terminal:

    % killall -ABRT Test821002
    

    replacing Test821002 with the name of your app.

  3. Wait for the crash report window to show up, and then dismiss that.

  4. Relaunch the app.

You should see the diagnostic payload delivered promptly.

Please try this and let me know what happens.

ps I realise that you’re primarily interested in metric payloads rather than diagnostic payloads. The purpose of this test is to confirm whether the basics are working. If they are, we can return to the metric payload issue. But if this diagnostic payload test fails, then we need to understand why before we start looking at metric payloads.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi,

We installed again via the same full PKG installer. On the very first launch after this second install, we received approximately 48 hours of past metric payloads through pastPayloads. We are not sure what was different between the two full installs. Is there something on the system side that could cause the first install to not deliver payloads, and then a subsequent install to start delivering them?


Diagnostic payload test results — following your suggested steps:

Host app (UI process):

  1. Launched the app
  2. Ran: `killall -ABRT HostApp Process
  3. Dismissed the crash report window
  4. Relaunched the app

didReceiveDiagnosticPayloads: was called promptly on relaunch.

Network Extension (tunnel process):

  1. Ran: `sudo killall -ABRT NEProcess
  2. Dismissed the crash report window
  3. Waited for the tunnel to restart automatically

❌ No diagnostic payload was delivered. didReceiveDiagnosticPayloads: was never called in the NE's subscriber.

So, could u comment if MetricKit works for NE Process? Are they any additional steps to be followed to make it work for NE process?

we received approximately 48 hours of past metric payloads

Oh, interesting. I’m not 100% sure what’s going on here but this behaviour doesn’t surprise me. Internally, MetricKit uses an XPC activity to drive metric delivery [1], and XPC activities a very much a best effort kinda thing.

could u comment if MetricKit works for NE Process?

Well, I think you have pretty solid evidence that it doesn’t (-:

I’m going to talk about what’s going on in a sec, but before I do that I want to give you some concrete advice: If you want MetricKit to work in your NE system extension, I encourage you to file an enhancement request that describes your setup and why MetricKit is important to you. Once you’re done, please post your bug number here, just for the record.

As to why this doesn’t work, consider this:

% find /System/Library/LaunchDaemons /System/Library/LaunchAgents -name "*[^a-z]metrickitd*" 
/System/Library/LaunchAgents/com.apple.metrickitd.plist

The MetricKit ‘daemon’ is not actually a daemon but rather a launchd agent. That means it’s only available to programs running in some sort of user login session [2]. All system extensions run in the global context, and thus don’t have access to services vended by an agent.

For more about execution contexts on macOS, see TN2083 Daemons and Agents. It’s super old, but the fundamentals are still valid.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] You can see evidence for this its launchd property list:

% plutil -p /System/Library/LaunchAgents/com.apple.metrickitd.plist
{
  …
  "Label" => "com.apple.metrickitd"
  "LaunchEvents" => {
    "com.apple.xpc.activity" => {
      "com.apple.metrickitd.setup" => {
        "AllowBattery" => true
        "Delay" => 60
        "Priority" => "Maintenance"
        "Repeating" => false
        "RequireScreenSleep" => true
      }
    }
  }
  …
}

Be aware, however, that this is only a part of the story. The relationship between MetricKit and XPC activities is very much an implementation detail.

[2] The exact set of sessions is set by the LimitLoadToSessionType property in the launchd property list. You can learn more about this in the launchd.plist man page, along with TN2083. I’m not going to explore it here because it’s not relevant to your situation.

Need MetricKit Implementation details for MacOS background Application, mainly for
 
 
Q