30 Dec 2025 06:06 PM
Hi,
I have a Browser Synthetic monitor with a JavaScript step, where under certain conditions I explicitly fail the execution using:
api.fail("My message");What I’m trying to achieve is:
a metric that counts failures of the synthetic monitor,
grouped or filtered by the failure message ("My message").
In Data Explorer, I can find synthetic error/failure metrics that allow split by:
geolocation
synthetic event
error code
However, I don’t see any dimension related to the failure reason / message coming from api.fail().
Is it possible to create a metric that counts synthetic failures based on the custom message passed to api.fail()?
(e.g. count executions failed with "My message")
Is there a way in a JavaScript step to explicitly define an error code when calling api.fail() (similar to HTTP error codes), or is it always message-only?
If this is not supported directly:
what would be the recommended best practice for distinguishing different failure reasons inside a single synthetic monitor?
Thanks in advance for any guidance or examples!
Regards, Deni
Solved! Go to Solution.
30 Dec 2025 06:30 PM - edited 30 Dec 2025 06:31 PM
I have done something similar but for http synthetic.
During the failure, in the javascript step (post execution in HTTP), I call the metric api to ingest a data point with the error message as dimension.
And for question 2, I don't think you can define custom HTTP errors to be displayed as your synthetic HTTP result.
https://docs.dynatrace.com/docs/observe/digital-experience/synthetic-monitoring/browser-monitors/bro...
30 Dec 2025 07:12 PM
Example of javascript code to call the metric api (it can be the log ingest api too)
(async function () {
try {
const ENV_URL = 'https://<yourtenanthere>.live.dynatrace.com';
const INGEST_URL = `${ENV_URL}/api/v2/metrics/ingest`;
const API_TOKEN = '<yourtokenhere>';
const metricKey = 'custom.synthetic.browser.message';
const dimKey = 'message';
const dimValue = 'Your Message';
const gaugeValue = 1;
const timestamp = Date.now();
const line = `${metricKey},${dimKey}="${dimValue}" gauge,${gaugeValue} ${timestamp}`;
const res = await fetch(INGEST_URL, {
method: 'POST',
headers: {
'Authorization': `Api-Token ${API_TOKEN}`, // requires metrics.ingest scope
'Content-Type': 'text/plain; charset=utf-8'
},
body: line,
mode: 'cors',
cache: 'no-store'
});
const text = await res.text();
api.info(`Metric ingest HTTP status: ${res.status} ${res.statusText}`);
if (text) api.info(`Ingest response: ${text}`);
if (res.status === 202 || res.status === 400) {
api.finish();
} else {
api.fail(`Metric ingest failed: ${res.status} ${res.statusText}`);
}
} catch (err) {
api.fail(`Metric ingest error: ${err.message}`);
}
})();Regards
30 Dec 2025 07:24 PM
Thank you very much 🙂
I’ve just started to “fight” with this and I really appreciate the example you shared.
just one more question, currently my code on the javascript synthetic event is:
var text = document.documentElement.innerText;
var isFound = text.indexOf('text') > -1;
if (isFound) {
api.fail("My reason");
}
How to integrate this call, something like this?
<my code>
(async function () { ... })
or should insert my code somewhere inside the async function?
Thank you!
Regards, Deni
31 Dec 2025 10:50 AM
I have asked copilot to help me with that, since I am not a JS specialist at all, lol.
Instead of Metric API, I have asked to use the Log Ingestion API, since your reason may be a long string, and that will be hard to play with using metric dimensions... Log is easier.
That's the code:
(function () {
// --- Your current validation ---
var text = document.documentElement.innerText;
var isFound = text.indexOf('text') > -1;
if (!isFound) {
return; // nothing to log or fail
}
// --- Configuration (replace placeholders / inject securely) ---
const REASON = "My reason";
const SNAME = "Synthetic Name";
const ENV_URL = "https://<your-environment-id>.live.dynatrace.com";
const INGEST_URL = `${ENV_URL}/api/v2/logs/ingest`;
const API_TOKEN = "<logs.ingest-token>";
// Build a JSON payload (array of log events)
const payload = [{
content: REASON,
timestamp: new Date().toISOString(), // RFC3339 timestamp
severity: "error",
// Custom attributes (flat key-value pairs)
"log.source": "synthetic.browser",
"synthetic.name": SNAME
}];
// --- Send to Dynatrace Logs ingest, then fail the step ---
(async () => {
try {
const res = await fetch(INGEST_URL, {
method: "POST",
headers: {
"Authorization": `Api-Token ${API_TOKEN}`, // token must have logs.ingest scope
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify(payload)
});
// Log ingestion response for troubleshooting (non-blocking)
if (!res.ok) {
const errText = await res.text().catch(() => "");
api.info(`Log ingest failed: HTTP ${res.status} ${errText}`);
}
} catch (err) {
// Network / CORS errors
api.info(`Log ingest error: ${err && err.message ? err.message : err}`);
} finally {
// Ensure the synthetic execution fails with the same reason
api.fail(REASON);
}
})();
})();Try it and let us know.
31 Dec 2025 01:43 PM
Hi @dannemca ,
Thank you very much 🙂
The reason I asked about using async is that initially I tried similar approaches myself, but I ran into a specific issue:
api.fail() was not behaving as expected. Instead of failing the JavaScript step, the synthetic execution continued and failed on the next step with a LOCATOR_ERROR.
That’s why I ended up writing the logic in a fully synchronous way. With that approach, the execution fails immediately in the JavaScript step and shows the correct failure reason (the one passed to api.fail()), which is what I was aiming for.
I tested the example you shared and it works well in terms of sending a log message, which can then be used for metrics (I’ll also try ingesting a metric directly, I assume that should work as well).
I also experimented with adding await to explicitly wait for the full result of the API call, but this doesn’t seem to be supported in a Synthetic Browser monitor. Using await results in a syntax error, so waiting synchronously for the HTTP response doesn’t appear to be possible in this context.
I should also mention that I’m not very strong in JavaScript and I’ve been using ChatGPT to help me with this. Interestingly, it has been telling me from the beginning that what I’m trying to achieve is fundamentally not possible in a fully reliable way within Synthetic monitors 🙂
What I plan to try next (and what I believe will work) is splitting this into two JavaScript steps:
One step with an asynchronous call that only sends the log message (this is already tested and works).
A second step, identical to the code in my initial comment, whose only responsibility is to stop the execution and prevent it from continuing to the next steps (this is also tested and works on its own).
The remaining limitation is error handling for the HTTP call itself.
If the HTTP request fails, I currently have no reliable way to detect or surface that failure:
api.info() doesn’t seem to be persisted anywhere I can query (I asked about this in another post and still don't have resolution).
api.fail() inside the catch block doesn’t execute early enough to fail the synthetic step as intended.
So at the moment, handling HTTP-call failures explicitly inside a Synthetic Browser monitor remains an open challenge for me + if possible, to succeed implementing all this in one java script step.
Regards, Deni
Featured Posts