Google Custom Metrics tracking of latency data - google-cloud-platform

I'm using external service, based on REST calls, and I want to track the time that took the service to respond to my requests. My code is written with C# (core v2.2)
I planning to count times for all the HTTP requests (with Stopwatch) and keep this information in a List<long>. Every 60 seconds I will write the tracked information from the list to Google Custom Metrics.
In the end, I expect to see the AVERAGE time of execution in a graph.
This is my code so far:
public class CustomMetricsWritter
{
public CustomMetricsWritter(string projectId)
{
this.Client = MetricServiceClient.Create();
this.ProjectId = projectId;
}
private MetricServiceClient Client { get; set; }
public string ProjectId { get; private set; }
public object CreateMetric(string metricType, string title, string description, string unit)
{
// Prepare custom metric descriptor.
MetricDescriptor metricDescriptor = new MetricDescriptor();
metricDescriptor.DisplayName = title;
metricDescriptor.Description = description;
metricDescriptor.MetricKind = MetricKind.Gauge;
metricDescriptor.ValueType = MetricDescriptor.Types.ValueType.Double;
metricDescriptor.Type = metricType;
metricDescriptor.Unit = unit;
CreateMetricDescriptorRequest request = new CreateMetricDescriptorRequest
{
ProjectName = new ProjectName(this.ProjectId),
};
request.MetricDescriptor = metricDescriptor;
// Make the request.
return Client.CreateMetricDescriptor(request);
}
public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public async Task WriteTimeSeriesDataAsync(string metricDescriptor, TypedValue[] points, string machineName)
{
// Initialize request argument(s).
ProjectName name = new ProjectName(this.ProjectId);
// Prepare a data point.
Timestamp timeStamp = new Timestamp();
timeStamp.Seconds = (long)(DateTime.UtcNow - UnixEpoch).TotalSeconds;
TimeInterval interval = new TimeInterval();
interval.EndTime = timeStamp;
// Prepare monitored resource.
MonitoredResource resource = new MonitoredResource();
resource.Type = "global";
resource.Labels.Add("project_id", this.ProjectId);
// Add newly created time series to list of time series to be written.
List<TimeSeries> timeSeries = new List<TimeSeries>(points.Length);
// Prepare custom metric.
Metric metric = new Metric();
metric.Type = metricDescriptor;
metric.Labels.Add("machine", machineName);
// Create a new time series using inputs.
TimeSeries timeSeriesData = new TimeSeries();
timeSeriesData.Metric = metric;
timeSeriesData.Resource = resource;
foreach (var point in points)
{
Point dataPoint = new Point();
dataPoint.Value = point;
dataPoint.Interval = interval;
timeSeriesData.Points.Add(dataPoint);
}
timeSeries.Add(timeSeriesData);
// Write time series data.
await this.Client.CreateTimeSeriesAsync(name, timeSeries).ConfigureAwait(false);
}
}
I running this class with this code (create the metric and then fill it with dummy values):
try
{
CustomMetricsWritter customMetricsWriter = new CustomMetricsWritter(Consts.GOOGLE_CLOUD_PROJECT);
string metric = "custom.googleapis.com/web/latency";
customMetricsWriter.CreateMetric(metric, "Execution Latency", "Calling REST service (MS).", "{INT64}");
// Exception thrown in the next line ----->
await customMetricsWriter.WriteTimeSeriesDataAsync(
metric,
new TypedValue[] {
new TypedValue(){ Int64Value = 150},
new TypedValue(){ Int64Value = 250},
new TypedValue(){ Int64Value = 350},
},
"my-machine-type");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
I getting back this thrown exception:
Grpc.Core.RpcException: Status(StatusCode=InvalidArgument, Detail="One or more TimeSeries could not be written: Field timeSeries[0].points had an invalid value: Only one point can be written per TimeSeries per request.: timeSeries[0]")
at Google.Api.Gax.Grpc.ApiCallRetryExtensions.<>c__DisplayClass0_0`2.<<WithRetry>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at ***.CustomMetricsWritter.WriteTimeSeriesDataAsync(String metricDescriptor, TypedValue[] points, String machineName) in ***\GoogleCloud\CustomMetricsWritter.cs:line 131
at Test.Program.MainAsync() in ***\Test\Program.cs:line 156
What I'm doing wrong?

I'm not a C# expert, but there are examples here:
https://github.com/GoogleCloudPlatform/dotnet-docs-samples/blob/master/monitoring/api/QuickStart/QuickStart.cs

Related

Unit Test for Apex Trigger that Concatenates Fields

I am trying to write a test for a before trigger that takes fields from a custom object and concatenates them into a custom Key__c field.
The trigger works in the Sandbox and now I am trying to get it into production. However, whenever I try and do a System.assert/assertEquals after I create a purchase and perform DML, the value of Key__c always returns null. I am aware I can create a flow/process to do this, but I am trying to solve this with code for my own edification. How can I get the fields to concatenate and return properly in the test? (the commented out asserts are what I have tried so far, and have failed when run)
trigger Composite_Key on Purchases__c (before insert, before update) {
if(Trigger.isBefore)
{
for(Purchases__c purchase : trigger.new)
{
String eventName = String.isBlank(purchase.Event_name__c)?'':purchase.Event_name__c+'-';
String section = String.isBlank(purchase.section__c)?'':purchase.section__c+'-';
String row = String.isBlank(purchase.row__c)?'':purchase.row__c+'-';
String seat = String.isBlank(String.valueOf(purchase.seat__c))?'':String.valueOf(purchase.seat__c)+'-';
String numseats = String.isBlank(String.valueOf(purchase.number_of_seats__c))?'':String.valueOf(purchase.number_of_seats__c)+'-';
String adddatetime = String.isBlank(String.valueOf(purchase.add_datetime__c))?'':String.valueOf(purchase.add_datetime__c);
purchase.Key__c = eventName + section + row + seat + numseats + adddatetime;
}
}
}
#isTest
public class CompositeKeyTest {
public static testMethod void testPurchase() {
//create a purchase to fire the trigger
Purchases__c purchase = new Purchases__c(Event_name__c = 'test', section__c='test',row__c='test', seat__c=1.0,number_of_seats__c='test',add_datetime__c='test');
Insert purchase;
//System.assert(purchases__c.Key__c.getDescribe().getName() == 'testesttest1testtest');
//System.assertEquals('testtesttest1.0testtest',purchase.Key__c);
}
static testMethod void testbulkPurchase(){
List<Purchases__c> purchaseList = new List<Purchases__c>();
for(integer i=0 ; i < 10; i++)
{
Purchases__c purchaserec = new Purchases__c(Event_name__c = 'test', section__c='test',row__c='test', seat__c= i+1.0 ,number_of_seats__c='test',add_datetime__c='test');
purchaseList.add(purchaserec);
}
insert purchaseList;
//System.assertEquals('testtesttest5testtest',purchaseList[4].Key__c,'Key is not Valid');
}
}
You need to requery the records after inserting them to get the updated data from the triggers/database

How to start with IOTA application

i want to develop an IOTA application, but not a messaging application or coin based system. I want an simple example of how to store data in IOTA. For example i want to build an SCM or even an simple login/registration app. Can anyone guide me? Any sample application? i try to run https://github.com/domschiener/leaderboard-example But getting same error like https://github.com/domschiener/leaderboard-example/issues/6 How to run this.
Storing text data on the tangle is not that difficult. The following are snippets from my tangle-based app. I used IOTA's API Java wrapper library Jota.
1) Connect to IOTA node. You can find a list of nodes here https://nodes.iota.works. Also you can set up your own full node and use it instead of an external one.
final String protocol = "https";
final String url = "tuna.iotasalad.org";
final String port = "14265";
IotaAPI iotaServer = new IotaAPI.Builder().protocol(protocol).host(host).port(port).build();
2) Covert your text into trytes
String trytes = TrytesConverter.toTrytes("my text");
3) Prepare and send transaction to tangle
private static final String SEED = "IHDEENZYITYVYSPKAURUZAQKGVJERUZDJMYTANNZZGPZ9GKWTEOJJ9AAMXOGZNQLSNMFDSQOTZAEETA99";//just a random one
private static final int MIN_WEIGHT_MAGNITUDE = 14;
private static final int DEPTH = 9;
private static final int TAG = "mytag"; //optional
String tangleHash = prepareTransfer(createAddress(), trytes);
public String createAddress() throws ArgumentException {
GetNewAddressResponse res = iotaServer.getNewAddress(SEED, 2, 0, false, 1, false);
return res.getAddresses().get(0);
}
public String prepareTransfer(String address_seclevel_2, String trytes) throws ArgumentException {
List<Transfer> transfers = new ArrayList<Transfer>();
transfers.add(new Transfer(address_seclevel_2, 0, trytes, TAG));
SendTransferResponse str = iotaServer.sendTransfer(SEED, 2, DEPTH, MIN_WEIGHT_MAGNITUDE, transfers, null,
null, false, false);
if(str.getSuccessfully()!=null){
//Transfer successfully!
for(Transaction tx: str.getTransactions()) {
return tx.getHash();
}
}
return "Handle error here. Something went wrong!";
}

Using Mockito to test Java Hbase API

This is the method that I am testing. This method gets some Bytes from a Hbase Database based on an specific id, in this case called dtmid. The reason I why I want to return some specific values is because I realized that there is no way to know if an id will always be in Hbase. Also, the column Family and column name could change.
#Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
try {
if (tuple.size() > 0) {
Long dtmid = tuple.getLong(0);
byte[] rowKey = HBaseRowKeyDistributor.getDistributedKey(dtmid);
Get get = new Get(rowKey);
get.addFamily("a".getBytes());
Result result = table.get(get);
byte[] bidUser = result.getValue("a".getBytes(),
"co_created_5076".getBytes());
collector.emit(new Values(dtmid, bidUser));
}
} catch (IOException e) {
e.printStackTrace();
}
}
On my main class when this method is called I want to return a specific value. The method should return some bytes.
byte[] bidUser = result.getValue("a".getBytes(),
"co_created_5076".getBytes());
This is what I have on my Unit Test.
#Test
public void testExecute() throws IOException {
long dtmId = 350000000770902930L;
final byte[] COL_FAMILY = "a".getBytes();
final byte[] COL_QUALIFIER = "co_created_5076".getBytes();
//setting a key value pair to put in result
List<KeyValue> kvs = new ArrayList<KeyValue>();
kvs.add(new KeyValue("--350000000770902930".getBytes(), COL_FAMILY, COL_QUALIFIER, Bytes.toBytes("ExpedtedBytes")));
// I create an Instance of result
Result result = new Result(kvs);
// A mock tuple with a single dtmid
Tuple tuple = mock(Tuple.class);
bolt.table = mock(HTable.class);
Result mcResult = mock(Result.class);
when(tuple.size()).thenReturn(1);
when(tuple.getLong(0)).thenReturn(dtmId);
when(bolt.table.get(any(Get.class))).thenReturn(result);
when(mcResult.getValue(any(byte[].class), any(byte[].class))).thenReturn(Bytes.toBytes("Bytes"));
BasicOutputCollector collector = mock(BasicOutputCollector.class);
// Execute the bolt.
bolt.execute(tuple, collector);
ArgumentCaptor<Values> valuesArg = ArgumentCaptor
.forClass(Values.class);
verify(collector).emit(valuesArg.capture());
Values d = valuesArg.getValue();
//casting this object in to a byteArray.
byte[] i = (byte[]) d.get(1);
assertEquals(dtmId, d.get(0));
}
I am using this down here to return my bytes.For some reason is not working.
when(mcResult.getValue(any(byte[].class), any(byte[].class))).thenReturn(Bytes
.toBytes("myBytes"));
For some reason when I capture the values, I still get the bytes that I specified here:
List<KeyValue> kvs = new ArrayList<KeyValue>();
kvs.add(new KeyValue("--350000000770902930".getBytes(),COL_FAMILY, COL_QUALIFIER, Bytes
.toBytes("ExpedtedBytes")));
Result result = new Result(kvs);
How about replacing
when(bolt.table.get(any(Get.class))).thenReturn(result);
with...
when(bolt.table.get(any(Get.class))).thenReturn(mcResult);

Alfresco WS Client API - WSSecurityException when using fetchMore method

Can someone tell me what's wrong with my code here... I'm always getting this exception on the first call to contentService.read(...) after the first fetchMore has occurred.
org.apache.ws.security.WSSecurityException: The security token could not be authenticated or authorized
// Here we're setting the endpoint address manually, this way we don't need to use
// webserviceclient.properties
WebServiceFactory.setEndpointAddress(wsRepositoryEndpoint);
AuthenticationUtils.startSession(wsUsername, wsPassword);
// Set the batch size in the query header
int batchSize = 5000;
QueryConfiguration queryCfg = new QueryConfiguration();
queryCfg.setFetchSize(batchSize);
RepositoryServiceSoapBindingStub repositoryService = WebServiceFactory.getRepositoryService();
repositoryService.setHeader(new RepositoryServiceLocator().getServiceName().getNamespaceURI(), "QueryHeader", queryCfg);
ContentServiceSoapBindingStub contentService = WebServiceFactory.getContentService();
String luceneQuery = buildLuceneQuery(categories, properties);
// Call the repository service to do search based on category
Query query = new Query(Constants.QUERY_LANG_LUCENE, luceneQuery);
// Execute the query
QueryResult queryResult = repositoryService.query(STORE, query, true);
String querySession = queryResult.getQuerySession();
while (querySession != null) {
ResultSet resultSet = queryResult.getResultSet();
ResultSetRow[] rows = resultSet.getRows();
for (ResultSetRow row : rows) {
// Read the content from the repository
Content[] readResult = contentService.read(new Predicate(new Reference[] { new Reference(STORE, row.getNode().getId(), null) },
STORE, null), Constants.PROP_CONTENT);
Content content = readResult[0];
[...]
}
// Get the next batch of results
queryResult = repositoryService.fetchMore(querySession);
// process subsequent query results
querySession = queryResult.getQuerySession();
}

Dynamics GP Web Services: SalesInvoice Creation with Lot Allocation

I'm trying to use the following code to create a new SalesInvoice based on an existing SalesOrder:
SalesInvoice invoice = new SalesInvoice();
invoice.DocumentTypeKey = new SalesDocumentTypeKey { Type = SalesDocumentType.Invoice };
invoice.CustomerKey = originalOrder.CustomerKey;
invoice.BatchKey = originalOrder.BatchKey;
invoice.Terms = new SalesTerms { DiscountTakenAmount = new MoneyAmount { Value = 0, Currency = "USD", DecimalDigits = 2 }, DiscountAvailableAmount = new MoneyAmount { Value = 0, Currency = "USD", DecimalDigits = 0 } };
invoice.OriginalSalesDocumentKey = originalOrder.Key;
List<SalesInvoiceLine> lineList = new List<SalesInvoiceLine>();
for (int i = 0; i < originalOrder.Lines.Length; i++)
{
SalesInvoiceLine line = new SalesInvoiceLine();
line.ItemKey = originalOrder.Lines[i].ItemKey;
line.Key = new SalesLineKey { LineSequenceNumber = originalOrder.Lines[i].Key.LineSequenceNumber; }
SalesLineLot lot = new SalesLineLot();
lot.LotNumber = originalOrder.Lines[i].Lots[0].LotNumber;
lot.Quantity = new Quantity { Value = 2200 };
lot.Key = new SalesLineLotKey { SequenceNumber = originalOrder.Lines[i].Lots[0].Key.SequenceNumber };
line.Lots = new SalesLineLot[] { lot };
line.Quantity = new Quantity { Value = 2200 };
lineList.Add(line);
}
invoice.Lines = lineList.ToArray();
DynamicsWS.CreateSalesInvoice(invoice, DynamicsContext, DynamicsWS.GetPolicyByOperation("CreateSalesInvoice", DynamicsContext));
When executed, I receive the following error:
SQL Server Exception: Operation expects a parameter which was not supplied.
And the more detailed exception from the Exception Console in Dynamics:
Procedure or function 'taSopLotAuto' expects parameter '#I_vLNITMSEQ',
which was not supplied.
After a considerable amount of digging through Google, I discovered a few things.
'taSopLotAuto' is an eConnect procedure within the Sales Order Processing component that attempts to automatically fill lots. I do not want the lots automatically filled, which is why I try to fill them manually in the code. I've also modified the CreateSalesInvoice policy from Automatic lot fulfillment to Manual lot fulfillment for the GP web services user, but that didn't change which eConnect procedure was called.
'#I_vLNITMSEQ' refers to the LineSequenceNumber. The LineSequenceNumber and SequenceNumber (of the Lot itself) must match. In my case they are both the default: 16384. Not only is this parameter set in the code above, but it also appears in the SOAP message that the server attempted to process - hardly "not supplied."
I can create an invoice sans line items without a hitch, but if I add line items it fails. I do not understand why I am receiving an error for a missing parameter that is clearly present.
Any ideas on how to successfully create a SalesInvoice through Dynamics GP 10.0 Web Services?
Maybe you mess to add the line key to the lot:
lot.Key = new SalesLineKey();
lot.Key.SalesDocumentKey = new SalesDocumentKey();
lot.Key.SalesDocumentKey.Id = seq.ToString();