Skip to content

Requests

lordmilko edited this page May 12, 2021 · 41 revisions

Contents

C#

PrtgAPI supports four types of requests

Synchronous

Synchronous requests block the current thread until their results have returned

client.GetSensors();

Synchronous requests are ideal when writing a Console Application, or some other application where the PRTG processing has a thread to itself. Synchronous requests use an asynchronous HttpClient internally, however return to synchronous methods as soon as the request has completed.

When executing methods that take request parameters (such as GetSensors) as well as potentially long running methods (such as GetSensorTargets) it is possible to specify an optional CancellationToken to abort execution from another thread.

//Request sensor targets in conjunction with a CancellationToken from a CancellationTokenSource
var targets = client.Targets.GetSensorTargets(1001, "vmwaredatastoreextern", token: cts.Token);

All PowerShell cmdlets operate as synchronous requests.

Asynchronous

Asynchronous requests are implemented using await/async. All internal async calls are decorated with ConfigureAwait(false), and so are safe to use in Synchronization Contexts (such as WinForms and ASP.NET)

var sensors = await client.GetSensorsAsync();

All asynchronous methods support specifying an optional CancellationToken. Methods that contain any optional parameters will also contain an optional CancellationToken parameter, while all others will contain a separate CancellationToken specific overload.

var sensors = await client.GetSensorsAsync(cts.Token);

Stream

Stream requests allow you to process data from PRTG as it is returned, instead of waiting for all of the data to come back at once. This is performed by splitting large requests up into several smaller requests and returning the responses to the caller in the order they come in.

When you call a Stream method, PrtgAPI requests the total number of objects it will retrieve and then immediately returns an IEnumerable<T>. Only when the collection is enumerated are the object requests sent to the server. If PrtgAPI detects that more than 20,000 objects will be returned from the request, PRTG will execute requests serially to prevent too many requests from queuing up and timing out. Otherwise, all of the requests will be executed all at once. Internally, the IEnumerable<T> is a generator that forces a collection of Task<> objects to yield their results. As a result of this, Stream requests block their calling threads when they wait for more data.

var sensors = client.StreamSensors();

foreach(var sensor in sensors)
{
    Console.WriteLine(sensor.Name);
}

If you wish to reduce the load on your PRTG Server, you may manually specify that PRTG should stream serially. Sensor, Device, Group and Probe method overloads that support filtering (e.g. StreamDevices(Property property, object value)) do not support serial streaming. Serial streaming can be used however with a custom set of object parameters (that may include object filters).

// Serial stream all sensors
var sensors = client.StreamSensors(true);

// Serial stream a custom request, sorting all devices by their Object IDs ascending
var parameters = new DeviceParameters(new SearchFilter(Property.Name, "dc-1"))
{
    SortBy = Property.Id
};

var devices = client.StreamDevices(parameters, true);

Serial Stream requests do not need to request the total number of objects that exist when the Start property of the request parameters is null or 0, making Serial Stream requests an attractive option if you don't want to deal with this overhead.

Stream requests are supported on the following object types

Query

PrtgAPI implements a custom LINQ Provider, allowing complex API requests to be defined as part of the language, rather than having to specify enum values and SearchFilter objects to request parameters.

//With queries
var sensors = client.QuerySensors(s => s.Name == "Ping" && s.Device == "dc-1");

//With parameters
var sensors = client.GetSensors(
    new SearchFilter(Property.Name, "Ping"),
    new SearchFilter(Property.Device, "dc-1")
);

PrtgAPI supports the execution of arbitrarily complex queries, regardless of whether or not these queries are supported by PRTG. For any operation not supported by PRTG, PrtgAPI will extract the maximal legal expression that can be executed, then apply the remaining expressions client side against the server's response.

As a result of this hybrid nature, it is important to understand you get what you ask for, and that a significant performance impact can occur when executing an ambiguous query

//Returns a different number of results based on whether the element that
//was skipped had an Id > 4000
var ambiguousResults = client.QuerySensors().Skip(1).Where(s => s.Id > 4000);

//Returns the same number of results every time
var unambiguousResults = client.QuerySensors().Where(s => s.Id > 4000).Skip(1);

If you are interested to learn more about how queries work, see Query

Strict

By default, PrtgAPI tries to give you what you want. It is extremely permissible with what it will accept in an expression; barring manual Expression tree construction, PrtgAPI will accept any IQueryable expression considered legal by the compiler. However, in order to achieve this freedom PrtgAPI will freely disregard parts of your expression if it determines they cannot be executed server side, opting instead to filter a broader set of results client side.

While this may offer a greater degree of flexibility when programming, this can come at the cost of performance (requesting potentially many more records than may be actually desired) when an expression is not well formed. If you are writing an extremely performance sensitive application and wish to be protected from yourself, this can be achieved by instructing PrtgAPI to use strict evaluation mode

//NotSupportedException: non-consecutive Where call
client.QuerySensors(true)
    .Where(s => s.Id > 4000)
    .Select(s => s.Name)
    .Where(n => n.Name.Contains("Disk")
    .ToList();

When using strict mode, illegal query expressions must explicitly be marked as being executed client side. This can be achieved by chaining to Enumerable.AsEnumerable within your expression chain.

//Execute Where(1)/Select server side, Where(2) client side
client.QuerySensors(true)
    .Where(s => s.Id > 4000)
    .Select(s => s.Name)
    .AsEnumerable()
    .Where(n => n.Name.Contains("Disk")
    .ToList();

The following table outlines expressions that cannot be executed server side. In general, when constructing an expression, you can easily identify whether it will be valid by simply asking yourself: do you really think PRTG can evaluate that?

General Expressions

Expression Example
Unsupported LINQ methods .TakeWhile(s => s.Id < 4000)
Unconsecutive LINQ methods .Where(s => s.Id > 1).Select(s => s.Name).Where(n => n == "Ping")
Missing property expressions .Where(s => false && s.Name == "Ping")
Multiple property references .Where(s => s.Name == s.Message)
AND same property .Where(s => s.Name.Contains("Pi") && s.Name.Contains("ng"))
OR different properties .Where(s => s.Id == 4000 || s.Name == "Ping")
Unsupported expression type .Where(s => s.Tags.Length == 3)
Unsupported sub-member .Where(s => s.LastUp.Value.Day == 30)
Unsupported cast .Where(s => ((CustomType)s.Id) == 3000)
Unsupported method .Where(s => CheckId(s.Id))
Unsupported type ToString .Where(s => s.LastUp.ToString().Contains("2018"))
Unsupported filter operator .Where(s => s.Name != "Ping") //Strings don't support !=
Unsupported filter value .Where(s => s.LastUp == null)
Unsupported equality .Where(s => ((Enum) s.Status).Equals((Enum) RetryMode.Retry))
Unwrappable expression .Where(s => (bool?)(s.Id == 4000) == false)
Merge multiple parameters .Where(s => s.Id == 1).Where((s, i) => s.Name == "abc" && i == 0)
No corresponding Property .Where(s => s.DisplayLastValue == "1 MB")
Ambiguous intermediate .Select(s => new Val(s.ParentId, s.Id)).Where(v => v.RealId == 1)
Ambiguous condition .Where(s => ((bool)(object)(s.Id == 1)).ToString().Contains("T"))

Log Expressions

Expression Example
Multiple start dates .Where(l => l.DateTime < today && l.DateTime < yesterday
Multiple end dates .Where(l => l.DateTime > lastWeek && l.DateTime > yesterday
Unsupported filters .Where(l => l.Name == "Ping")
Multiple IDs .Where(l => l.Id == 1001 || l.Id == 1002)
Multiple ranges .Where(l => l.DateTime > a && l.DateTime < b || l.DateTime < c)

PowerShell

Batch

Certain operations allow you to operate against multiple Object IDs in a single request. This can provide a substantial performance improvement over executing an individual request for each item. Care should be taken when processing multiple operations in a single request, as there will be a much shorter time frame during which execution can be aborted in the event you have made a mistake.

// Pause the objects with IDs 1001, 1002 and 1003 forever (C#)
client.PauseObject(new[] {1001, 1002, 1003});

All State and Property Manipulation cmdlets as well as Rename-Object operate in batch mode by default. This can be overridden by specifying -Batch:$false

# Pause the sensors with IDs 1001, 1002 and 1003 forever, one at a time
Get-Sensor -Id 1001,1002,1003 | Pause-Object -Forever -Batch:$false

If Remove-Object is invoked with -Force and -Batch:$false has not otherwise been specified, Remove-Object will automatically enable batch mode to speed up the request. If Remove-Object is invoked without any parameters, PrtgAPI will prompt for confirmation and execute removal one by one.

# Batch
Get-Sensor ping | Remove-Object -Force

# Serial
Get-Sensor ping | Remove-Object -Force -Batch:$false

# Prompt (Serial)
Get-Sensor ping | Remove-Object

PrtgAPI will process up to 1500 objects per request. If more than 1500 objects are detected, PrtgAPI will split the request into multiple batches (1501 objects = 2 requests, 3001 objects = 3 requests, etc)

PassThru

When piping commands together in PowerShell, it is sometimes desirable to perform multiple operations against a single source object (for example, unpausing an object and then refreshing it). While this can be achieved by executing multiple separate commands, it is also possible to perform multiple operations in a single command via the -PassThru parameter.

When -PassThru is specified, PrtgAPI will output the input object of a cmdlet for use in the next cmdlet in the pipeline.

# Resume and refresh all paused WMI CPU Load sensors
Get-Sensor -Tags wmicpu* -Status Paused | Resume-Object -PassThru | Refresh-Object

Objects are output from operation cmdlets as they are processed. As such, when operating in Batch Mode, objects will be output to the next cmdlet only after all input objects have been read.

Note that when executing in -PassThru mode, PrtgAPI always outputs the original object that was piped into the cmdlet. This is due to the fact that API requests are handled asynchronously by PRTG; as such, by the time a request has completed, there is no guarantee that the target object has updated when you attempt to re-retrieve it.

Clone this wiki locally