Anyone handled logging after UiPath Cloud migration? Sharing what we did with Splunk

While We’re in the middle of moving from on-prem Orchestrator to UiPath Cloud, and one thing that caught us completely off guard was how logging behaves after the migration.

When we were on-prem, it was simple —
Orchestrator had physical log files on the server, and Splunk UF(Universal Forwarder) just picked them up from the filesystem.
No fancy workarounds.

After moving to Cloud, that whole path is gone.
No filesystem → no UF → no way to grab logs directly.
Audit/compliance teams immediately started panicking because they rely on full log trails, not just what’s visible in Orchestrator UI.

We looked at the Cloud API approach, but honestly:

  • API limits
  • throttling
  • pagination
  • latency

made it pretty unreliable at scale for our unattended workloads.

So we ended up taking a completely different approach.

What we actually did (in simple terms)

Instead of depending on Orchestrator logs, we pushed logging down to the robot level.

  1. We updated the ‘NLog.config’ on each robot to generate logs locally in a structure that Splunk can read without extra parsing.
    (Basically made the layout closer to Splunk JSON format.)
  2. Installed Splunk Universal Forwarder on every unattended robot VM.
  3. UF now reads the robot-generated logs straight from the machine and ships them to Splunk.

So Splunk gets everything directly from the robot, not from Orchestrator.

This worked surprisingly well because:

  • Robots always generate logs
  • Cloud doesn’t block anything
  • No dependency on UiPath API
  • No throttling
  • No log delays
  • Cloud outages don’t affect log availability

Only thing to watch is local log cleanup — we automated that with a simple rotation script.

Sharing this because I’m sure others will hit the same issue

My question to the community:

Has anyone else tried robot-level logging for Cloud?
Any better way to handle this without touching API limits?

Would also be interested if anyone added extra fields to the robot logs that helped in Splunk searches (like more metadata, business identifiers, etc.).

Not sure if this is the “officially recommended way,” but it’s been stable so far for us.

Happy to share the config snippets if someone is trying something similar.

– Ravindra

@tracydixon @Lahiru.Fernando @Reda_Marzouk @nisargkadam23 @jjes @anob @Roman-Routinuum @hugo.drecourt @AndersJensen @Krollythegoalie @darylaw @Chris_Bolin @Mason_Turvey_Silver @dokumentor

1 Like

Hi @ravindra.reddy33,

This has plagued us as well. In addition deciphering encrypted logs in Splunk is also another challenge. There is very little use of the logs sent to Splunk if they are encrypted.

As I remember orchestrator logs are richer than robot level logs. You may miss some metrics in Splunk. Sadly I don’t have access to check this myself, but I remember earlier we had to use dedup on source key i Splunk index and orchestrator logs were the ground truth and we ignored robot source.

In short, orchestrator logs are complete but robot logs are substandard.

My advice to the team was to use a robot to go collect all logs for the last 3 hours and dump it on a robot machine. Splunk forwarder is smart enough to check for overlapped log lines, if any.

Sadly this means data utgress cost on the Automation Cloud instance. Tradeoff is there are no changes to indexing or Splunk queries.

Again, it’s worth to check the json keys in robot log and orchestrator logs to confirm this.

Hi @jeevith ,

Thanks for sharing this. You’re right — the robot-level logs definitely don’t have the same depth as the orchestrator logs. We also noticed gaps especially around:

  • queue transaction metadata
  • retry/exception context
  • some business fields
  • job-level properties that only orchestrator exposes

We also ran into several Cloud API limitations — mainly throttling during peak load, pagination gaps, missing or delayed fields, and no access to any physical log files. For our volume, the API approach became too unreliable, especially with parallel robots where 429s and incomplete batches were common

Your suggestion of using a robot to periodically pull the last few hours of logs is interesting — “We didn’t go that route mainly because of the Automation Cloud egress cost, which would increase significantly if we pulled logs down frequently. But it’s still a cleaner way to preserve the “truth source” if orchestrator logs are the priority.

Right now, our Splunk dashboards rely mostly on robot-level logs + custom metadata we added through NLog, which is enough for ops troubleshooting but might not be perfect for long-term audit.

I’ll definitely re-compare the JSON keys from robot vs orchestrator logs.
If you’ve seen any specific fields that are consistently missing from robot logs, that would be helpful.

Appreciate your input — glad to know others ran into the same problem.

You could make a Splunk logging scope, that could capture all the logs, as they are generated, when an automation is running. You’d need it as a top level activity in all your processes, but it would capture all of the logging information (including custom fields) as they are generated and skip anything with the API etc.

You need to hook into the Robot Executor and register another Log Listener.
The logging in the Executor can be subscribed to so your listener can listen for logs being generated and then do whatever you want with them. Thats how it under the hood already logs things to the Orchestrator, Studio and locally at the same time.

I use it in this activity scope to display the logs on the screen for unattended and attended runs when you cannot see the logs when the robot is running but want to see whats happening

Hi @Jon_Smith ,

Thanks for sharing this — I’ve looked at executor-level listeners before, and the approach definitely works in smaller estates or when you can modify the workflows.

Our challenge is that we have a very large number of automations, and using a custom logging scope or listener would require a code change in every process. With hundreds of workflows, that becomes a massive effort in terms of updates, regression testing, and ongoing maintenance.

The other concern is long-term versioning. Any Studio/Robot compatibility change or logging upgrade would force another round of updates across the whole estate. We’re trying to avoid adding more dependencies inside the processes themselves, since it increases the footprint and the maintenance surface.

Our main goal was to keep observability independent of the process logic and avoid modifying working automations. That’s why we leaned toward a solution that works transparently at the robot level without touching the workflows or requiring ongoing updates.

But I’ll check out the Ciphix scope — it’s interesting for smaller or more modular projects. Appreciate the suggestion.

Thats a fair concern and indeed if you need to update alot the scope could be an issue, so no arguments there.

The scope I made should be fairly robust when it comes to version updates as it uses reflection to avoid tightly coupling it to a specific executor version, that being said, when I first made a version of this several years ago and then tried to use it on an earlier Robot version I did end up with an error because I was trying to use a logging field that didnt exist on the earlier robot version, so testing the version before you deploy is indeed important.

I still think its do-able, and I work with small and large organizations, but do understand the concern. It depends on where you want to compromise. If you use the local logs then its harder to separate them I think, aswell as losing some of the data. If you want to go the API scraping route then you have to deal with rate limits which might mean you really need to dedicate a licence to do it constantly if the volume of logs is high enough.
My way would replicate the quality of the original, but comes with needing to add this scope to all projects. Its a compromise in either scenario.

It would be cool if we could add more logging options into the Robot, a way to hook in and add something like this without needing to modify every robot but instead doing it on the machine level.

@Jon_Smith

Really appreciate the detailed explanation — and you’re right, every option comes with a compromise somewhere.

For us, the main blocker wasn’t the listener approach itself, but the operational footprint of rolling it out across a very large automation environment. Even with reflection-based resilience, we’d still have to touch every workflow and maintain that footprint over time. In our environment, version drift happens often enough that even a small mismatch can create a long tail of issues, so minimizing “in-process” dependencies has become a big priority.

We also evaluated the API route, but like you mentioned, the rate limits and the volume of our unattended runs made it unrealistic unless we dedicated a robot just to scrape logs constantly. That defeats our goal of keeping the observability layer independent of execution.

On the robot-level logging side, we’ve been able to tune the NLog configuration at the machine/server level so the logs contain essentially the same fields we use from Orchestrator. That keeps the structure consistent without modifying any workflows, which has been a big advantage for us in terms of maintenance.

I completely agree with your point — the ideal world would be having the ability to register custom log listeners at the machine/robot service level. That would solve all of this without modifying automations or hitting API limits. Hopefully something UiPath considers down the line.

Thanks again for sharing your perspective — it’s helpful to hear how others approach this in different environments.

– Ravindra

1 Like