1

I am developing a custom processor in which I want to read value of Nifi counters. Is there a way to read Counters' value other than using Nifi Rest Api "http://nifi-host:port/nifi-api/counters"?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Sourav Gulati
  • 1,359
  • 9
  • 18

3 Answers3

4

No. Apache NiFi doesn't have any straightforward APIs available to read the counter value programmatically. An easy approach would be to use GetHTTP processor and use the NiFi REST API URL that you had mentioned: http(s)://nifi-host:port/nifi-api/counters.

Then use EvaluateJsonPath to just parse and read the counter value from the response JSON received from the GetHTTP processor.

Sivaprasanna Sethuraman
  • 4,014
  • 5
  • 31
  • 60
3

Based on Andy's suggestion, I have used refelection to read Counters as follows:

private void  printCounters(ProcessSession session) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class standardProcessSession=session.getClass();
        Field fieldContext = standardProcessSession.getDeclaredField("context");
        fieldContext.setAccessible(true);
        Object processContext =  fieldContext.get(session);
        Class processContextClass = processContext.getClass();
        Field fieldCounterRepo = processContextClass.getDeclaredField("counterRepo");
        fieldCounterRepo.setAccessible(true);
        Object counterRepo = fieldCounterRepo.get(processContext);
        Method declaredMethod = counterRepo.getClass().getDeclaredMethod("getCounters");

        ArrayList<Object> counters = (ArrayList<Object>)declaredMethod.invoke(counterRepo);
        for(Object obj:counters) {
            Method methodName = obj.getClass().getDeclaredMethod("getName");
            methodName.setAccessible(true);
            Method methodVal = obj.getClass().getDeclaredMethod("getValue");
            methodVal.setAccessible(true);
            System.out.println("Counter name: "+methodName.invoke(obj));
            System.out.println("Counter value: "+methodVal.invoke(obj));
        }
    }

NOTE: NIFI Version is 1.5.0

Sourav Gulati
  • 1,359
  • 9
  • 18
  • Hi @Sourav Gulati, can you help me with you code? I novice in NiFi and Java. I need function, which return value specific counter. How I can do it with your code? – Антон Букреев Jun 21 '20 at 15:23
  • 1
    I think you can make a small change to this function as follows: 1. Pass counter name as an argument to the function 2. Inside for loop check if counter name matches the name passed in the argument then return the value – Sourav Gulati Jun 22 '20 at 20:29
  • Hi @Sourav Gulati! I modified your function, everything works. My flow looks like this now `Generate Flow File-Update Counter (+1) - Update Counter(-1) - ExecuteScript`. I don't get the final value, but from the last `UpdateCounter` (the value is always `-n`). How can I get the final counter value from the `All Update Counters`context? – Антон Букреев Jun 25 '20 at 13:43
  • I change method `getName` on `getIdentifier` and it worked. – Антон Букреев Jun 25 '20 at 14:44
2

While it is not as easy to read/write counter values as it is to modify flowfile attributes, Apache NiFi does have APIs for modifying counters. However, the intent of counters is to provide information to human users, not for processors to make decisions based on their values. Depending on what you are trying to accomplish, you might be more successful using local maps or DistributedMapCacheServer and DistributedMapCacheClientService. If the values are only relevant to this processor, you can just use an in-memory map to store and retrieve the values. If you need to communicate with other processors, use the cache (example here).

Pierre Villard has written a good tutorial about using counters, and you can use ProcessSession#adjustCounter(String counter, int delta, boolean immediate) to modify counter values. Because counters were not designed to allow programmatic there is no way to retrieve the CounterRepository instance from the RepositoryContext object. You may also want to read about Reporting Tasks, as depending on your goal, this may be a better way to achieve it.

Andy
  • 13,916
  • 1
  • 36
  • 78
  • Hi Andy, Thanks for your reply. What I want to try is read all the counter values and store their state at times. 'adjustcounter' method can help to increment and decrement counters but cant retrieve the current values of counters. However, I am still confused what is the harm in providing a method that can read values of a counter when we can provide a method to adjust its values. – Sourav Gulati May 15 '18 at 02:20
  • What processors are setting the counter values, what do they represent, and what actions are taken on those values? I think a custom reporting task is probably the right solution here. – Andy May 15 '18 at 02:22
  • So Andy, if I want to read counters' values then rest API and Nifi UI is the only way? – Sourav Gulati May 15 '18 at 02:54
  • Theoretically you could use reflection to change the scope on the `StandardProcessSession` accessors to gain access to the `CounterRepository`, but yes, the REST API is probably the easiest way. Again, you didn’t answer any of my questions about how you intend to use these values, so I can’t offer more input. – Andy May 15 '18 at 02:56
  • Actually, We are planning to save these values for now only. These are the counters of some of our own processors which we are running in Apache Nifi to process some of our custom flows. We have not yet completely decided how we want to use these values though. It is more for monitoring purposes as of now. We want to automate this process of saving current counters for our processors. – Sourav Gulati May 15 '18 at 03:00
  • I strongly recommend that you use the `DistributedMapCache` in that case. Counters were not designed for the use case you are proposing, and as the originating processors are custom processors under your control, this will be the most performant and successful use case. – Andy May 15 '18 at 03:03
  • Thanks Andy :) Appreciate your suggestions – Sourav Gulati May 15 '18 at 03:05
  • Strange that if I print name if class it shows org.apache.nifi.controller.repository.StandardProcessSession. but while using reflection i get this error java.lang.NoClassDefFoundError: org/apache/nifi/controller/repository/StandardProcessSession. Any suggestion? – Sourav Gulati May 19 '18 at 16:33
  • You are likely trying to access that class from within a different classloader. NiFi has classloader isolation to allow different processors to run simultaneously with varied dependencies. – Andy May 21 '18 at 23:08