Update ObservableGauge in Open Telemetry Python - python

I am using opentelemetry-api 1.14 and opentelemetry-sdk 1.14. I know how to create and use Counter and ObservableGauge instruments. However, I need to update and set the gauge throughout my application in a similar manner to how a counter can use its add method. I have working code below but in this working code the gauge is static at 9.
import time
""" API is the interface that you should interact with."""
from opentelemetry import metrics
"""
SDK is the implementation. Only access SDK during initialization, startup, and shutdown.
"""
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.resources import Resource
def initialize():
resource = Resource(attributes={"service.name": "otel-test"})
readers = []
# Console Exporter
exporter = ConsoleMetricExporter()
reader1 = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
readers.append(reader1)
provider = MeterProvider(metric_readers=readers, resource=resource)
metrics.set_meter_provider(provider)
initialize()
provider = metrics.get_meter_provider()
meter = provider.get_meter("my-demo-meter")
simple_counter = meter.create_counter("simple_counter", description="simply increments each loop")
# Async Gauge
def observable_gauge_func(options):
yield metrics.Observation(9, {})
simple_gauge = meter.create_observable_gauge("simple_gauge", [observable_gauge_func])
# How can I update simple_gauge in main
def main():
loop_counter = 0
while True:
print(loop_counter)
loop_counter += 1
simple_counter.add(1)
# How can I update simple_gauge here?
time.sleep(5)
main()

I'm not sure if this is the best pattern for implementing an ObservableGauge instrument but this a method I used to implement it for my application given the requirements described in my question (i.e. update gauge in main function). Its worth providing given how few examples of ObservableGauge instrumentation there are online.
import time
import random
""" API is the interface that you should interact with."""
from opentelemetry import metrics
"""
SDK is the implementation. Only access SDK during initialization, startup, and shutdown.
"""
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.resources import Resource
def initialize():
resource = Resource(attributes={"service.name": "otel-test"})
readers = []
# Console Exporter
exporter = ConsoleMetricExporter()
reader1 = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
readers.append(reader1)
provider = MeterProvider(metric_readers=readers, resource=resource)
metrics.set_meter_provider(provider)
initialize()
provider = metrics.get_meter_provider()
meter = provider.get_meter("my-demo-meter")
simple_counter = meter.create_counter("simple_counter", description="simply increments each loop")
def create_simple_gauge(signal):
# Async Gauge
def observable_gauge_func(options):
yield metrics.Observation(signal.get_current_value(), {"simple_attribute": signal.attribute})
simple_gauge = meter.create_observable_gauge("simple_gauge", [observable_gauge_func])
return simple_gauge
class Signal:
def __init__(self, attribute):
self.attribute = attribute
def set_current_value(self, i):
self.current_value = i
def get_current_value(self):
return self.current_value
# How can I update the simple_gauge here?
def main():
loop_counter = 0
simple_signal = Signal("simple_attribute")
create_simple_gauge(simple_signal)
while True:
print(loop_counter)
loop_counter += 1
simple_counter.add(1)
randint = random.randint(0, 5)
print(randint)
simple_signal.set_current_value(randint)
time.sleep(5)
main()

Related

python setting state of a future

Is it bad practice to set the state of future to pass arguments?
Specifically using something like future.q = q to use q in the callback
from threading import Thread
from threading import RLock
from threading import current_thread
from concurrent.futures import Future
import time
import random
class NonBlockingQueue:
def __init__(self, max_size):
self.max_size = max_size
self.q = []
self.q_waiting_puts = []
self.q_waiting_gets = []
self.lock = RLock()
def enqueue(self, item):
future = None
with self.lock:
curr_size = len(self.q)
# queue is full so create a future for a put
# request
if curr_size == self.max_size:
future = Future()
self.q_waiting_puts.append(future)
else:
self.q.append(item)
# remember to resolve a pending future for
# a get request
if len(self.q_waiting_gets) != 0:
future_get = self.q_waiting_gets.pop(0)
future_get.set_result(self.q.pop(0))
return future
def retry_enqueue(future):
print("\nCallback invoked by thread {0}".format(current_thread().getName()))
item = future.item
q = future.q
new_future = q.enqueue(item)
if new_future is not None:
new_future.item = item
new_future.q = q
new_future.add_done_callback(retry_enqueue)
else:
print("\n{0} successfully added on a retry".format(item))
### MAIN CODE
def producer_thread(q):
item = 1
while 1:
future = q.enqueue(item)
if future is not None:
future.item = item
future.q = q
future.add_done_callback(retry_enqueue)
item += 1
# slow down the producer
time.sleep(random.randint(1, 3))
It is not a good idea to pass around arguments like this.
The reason is that in future (no pun), they could just disallow setting custom attributes on the Future object, which will break your code.
Better solution is to use functools.partial or lambda to pass extra arguments to the callback.
First, accept q as an argument in the retry_enqueue function:
def retry_enqueue(future, q): # accept 'q' argument
...
Example using functools.partial:
import functools
future.add_done_callback(functools.partial(retry_enqueue, q=q))
Example using lambda:
future.add_done_callback(lambda future: retry_enqueue(future, q))

Code runs in debug mode but not in normal [duplicate]

This question already has an answer here:
PyQt5 - QThread: Destroyed while thread is still running
(1 answer)
Closed 1 year ago.
I working on software, that includes parts working with the WHO ICD11 API.
When I run the code:
import json
import re
import threading
import time
from typing import Dict, List
import qdarkgraystyle as qdarkgraystyle
import requests
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtWidgets import QTreeView
from threading import Lock
from gui import Ui_MainWindow
import auth
printlock = Lock()
p = print
def print(*a, **b):
with printlock:
p(*a, **b)
class g:
max = 1
progress = 0
end_workers = False
loaded_dict = None
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super(ApplicationWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def linearization_url():
return f"https://id.who.int/icd/release/{ui.dropRevision.currentText().split('/')[0]}/{ui.dropRelease.currentText()}{('/' + ui.dropRevision.currentText().split('/')[1]) if len(ui.dropRevision.currentText().split('/')) > 1 else ''}"
def download():
loader = Loader(linearization_url())
loader.statusUpdateHook.connect(updatehook)
loader.statusFinished.connect(finishedLoader)
loader.start()
def updatehook():
ui.progress.setTextVisible(True)
ui.progress.setMaximum(gl.max)
ui.progress.setValue(gl.progress)
def finishedLoader():
json.dump(gl.loaded_dict, open("dict.json"), indent=4)
def split_link(url: str) -> Dict[str, str]:
return re.search(
"https?://id.who.int/icd/release/(?P<revision>[0-9]{2})/(?P<release>[^/]*)(/(?P<linearization>.*))?",
url).groupdict()
def worker(loader):
print("Worker booting...")
_token = gl.token
over = True
while not gl.end_workers:
url = ""
with loader.index_lock:
try:
url = loader.working_list[loader.index]
loader.index += 1
except IndexError:
over = False
if over:
json = request_json(url, _token)
with loader.finished_count_lock:
loader.working_dict[url] = json
if "child" in json:
for child in json["child"]:
loader.working_list.append(child)
loader.finished_count += 1
else:
over = True
def loadReleases():
token = getToken(auth.id, auth.secret)
ui.dropRelease.clear()
ui.dropRelease.repaint()
for release in request_json("https://id.who.int/icd/release/" + ui.dropRevision.currentText(), token)[
"release"]:
ui.dropRelease.addItem(split_link(release)["release"])
def getToken(clientID, clientSecret) -> str:
return requests.post('https://icdaccessmanagement.who.int/connect/token',
data={'client_id': clientID, 'client_secret': clientSecret, 'scope': 'icdapi_access',
'grant_type': 'client_credentials'}).json()['access_token']
def request_json(link_: str, token_: str):
headers_ = {
'Authorization': 'Bearer ' + token_,
'Accept': 'application/json',
'Accept-Language': 'en',
'API-Version': 'v2'
}
return requests.get(link_, headers=headers_).json()
class Loader(QtCore.QThread):
statusFinished = QtCore.pyqtSignal()
statusUpdateHook = QtCore.pyqtSignal()
index = 0
finished_count = 0
working_list = []
working_dict = {}
index_lock = Lock()
finished_count_lock = Lock()
workers = []
def __init__(self, lurl: str):
super().__init__()
self.working_list.append(lurl)
def progressUpdate(self):
gl.max = len(self.working_list)
gl.progress = self.finished_count
self.statusUpdateHook.emit()
def run(self):
for i in range(0, 20):
self.workers.append(threading.Thread(target=worker, args=(self,)))
self.workers[i].start()
while self.finished_count < len(self.working_list):
with self.index_lock:
with self.finished_count_lock:
self.progressUpdate()
time.sleep(5)
for work in self.workers:
if work.isAlive():
gl.end_workers = True
gl.loaded_dict = self.working_dict
self.statusFinished.emit()
if __name__ == "__main__":
import sys
gl = g()
gl.token = getToken(auth.id, auth.secret)
tabs: List[QTreeView] = []
app = QtWidgets.QApplication(sys.argv)
application = ApplicationWindow()
application.setStyleSheet(qdarkgraystyle.load_stylesheet())
ui = application.ui
ui.buttonDownload.clicked.connect(download)
ui.dropRevision.addItems(["10", "11/mms"])
ui.dropRevision.currentIndexChanged.connect(loadReleases)
loadReleases()
application.show()
sys.exit(app.exec_())
in Pycharms debug mode, it does, what I want it to. It works fine as long as it is in debug mode, while when in normal mode, when the buttonDownload.clicked event is triggered, the whole program crashes with the only output being:
QThread: Destroyed while thread is still running
Has anyone any idea on how to fix that?
(For reproducing purposes: You need API keys to access the API. They are imported from auth as auth.id and auth.secret. ID and secret can be obtained from an account over the WHO ICD11 API site)
Loader inherits QThread, in download function QThread object is bound to local variable loader, on exiting function this variable got garbage collected and bound object gets destroyed. You need to make sure loader variable outlive function, for example make it global variable or return it from function and store somewhere.

How to communicate with multiple servers using the single class defined in python and run it parallel

I have a python class that communicates with a server. That python class has many functions, i was using few functions. But i want to access multiple servers data at same time using the python class i have.
Am trying something like this, but it'll run one after the other. And i wanted to get the data at same time.
import threading
from server_class import server
class runMonitor(threading.Thread):
def __init__(self,func):
self.func = func
threading.Thread.__init__(self)
def run(self):
self.func()
def monitorSB(ipAddr):
sb = server(ipAddr)
sb.readInfo()
print ('\nReading Registers...\n')
sb.read_rx()
sb.read_tx()
i = 0
while(1):
if i == 0:
print 'Monitoring Registers...'
i = 1
sb.monitor_tx()
sb.monitor_rx()
t = runMonitor(monitorSB('192.168.10.78'))
q = runMonitor(monitorSB('192.168.10.101'))
t.start()
q.start()
print ('\nTest Done...\n')
In the above code, i wanted to access both servers at same time. Help me how to run in parallel
The thing is by saying monitorSB('192.168.10.78') it will execute it before passing it to your thread. Try this:
import threading
from server_class import server
class runMonitor(threading.Thread):
def __init__(self,func, param):
self.func = func
self.param = param
threading.Thread.__init__(self)
def run(self):
self.func(self.param)
def monitorSB(ipAddr):
sb = server(ipAddr)
sb.readInfo()
print ('\nReading Registers...\n')
sb.read_rx()
sb.read_tx()
i = 0
while(1):
if i == 0:
print 'Monitoring Registers...'
i = 1
sb.monitor_tx()
sb.monitor_rx()
t = runMonitor(monitorSB, '192.168.10.78')
q = runMonitor(monitorSB, '192.168.10.101')
t.start()
q.start()
print ('\nTest Done...\n')

How to use Python Queues in multiple functions

I am having an issue with accessing the queue object within my class in multiple functions, the following code might show you what i am trying to do;
import MySQLdb
import socket, sys
from struct import *
import threading
import sched, time
from datetime import datetime
from copy import deepcopy
import Queue
IP = {}
class QP:
def __init__(self):
self.jobs = Queue.Queue()
# this function runs every 10 seconds
# and processes any queued data.
def processQueue(self):
print(self.jobs.qsize())
time.sleep(5)
if self.jobs.empty():
print("No items in queue")
else:
IP_TEMP = {}
IP_TEMP = self.jobs.get()
IP_TEMP_QUEUE = {}
IP_TEMP_QUEUE = IP_TEMP
try:
cnx = #connect to database
cursor = cnx.cursor()
for k, v in IP_TEMP.iteritems():
#there is some code here, but its not the issue
try:
cursor.execute(add_packet, data_packet)
cnx.commit()
print("Task Done")
except:
print("Query failed, skipping")
break
except:
self.queueJobs(IP_TEMP_QUEUE)
IP = {}
self.jobs.task_done()
self.processQueue()
# this function is called by other modules to add data
def queueJobs(self, data):
self.jobs.put(data)
print(self.jobs.qsize())
return True
This is how i call the queueJobs method in the other modules:
self.process.queueJobs(IP_TEMP_QUEUE)
Now, the main issue is that the processQueue() function always returns that there is no jobs in the queue, even though they have been added below in the queueJobs() function.
Any help would be great!

Twisted Python getPage

I tried to get support on this but I am TOTALLY confused.
Here's my code:
from twisted.internet import reactor
from twisted.web.client import getPage
from twisted.web.error import Error
from twisted.internet.defer import DeferredList
from sys import argv
class GrabPage:
def __init__(self, page):
self.page = page
def start(self, *args):
if args == ():
# We apparently don't need authentication for this
d1 = getPage(self.page)
else:
if len(args) == 2:
# We have our login information
d1 = getPage(self.page, headers={"Authorization": " ".join(args)})
else:
raise Exception('Missing parameters')
d1.addCallback(self.pageCallback)
dl = DeferredList([d1])
d1.addErrback(self.errorHandler)
dl.addCallback(self.listCallback)
def errorHandler(self,result):
# Bad thingy!
pass
def pageCallback(self, result):
return result
def listCallback(self, result):
print result
a = GrabPage('http://www.google.com')
data = a.start() # Not the HTML
I wish to get the HTML out which is given to pageCallback when start() is called. This has been a pita for me. Ty! And sorry for my sucky coding.
You're missing the basics of how Twisted operates. It all revolves around the reactor, which you're never even running. Think of the reactor like this:
(source: krondo.com)
Until you start the reactor, by setting up deferreds all you're doing is chaining them with no events from which to fire.
I recommend you give the Twisted Intro by Dave Peticolas a read. It's quick and it really gives you all the missing information that the Twisted documentation doesn't.
Anyways, here is the most basic usage example of getPage as possible:
from twisted.web.client import getPage
from twisted.internet import reactor
url = 'http://aol.com'
def print_and_stop(output):
print output
if reactor.running:
reactor.stop()
if __name__ == '__main__':
print 'fetching', url
d = getPage(url)
d.addCallback(print_and_stop)
reactor.run()
Since getPage returns a deferred, I'm adding the callback print_and_stop to the deferred chain. After that, I start the reactor. The reactor fires getPage, which then fires print_and_stop which prints the data from aol.com and then stops the reactor.
Edit to show a working example of OP's code:
class GrabPage:
def __init__(self, page):
self.page = page
########### I added this:
self.data = None
def start(self, *args):
if args == ():
# We apparently don't need authentication for this
d1 = getPage(self.page)
else:
if len(args) == 2:
# We have our login information
d1 = getPage(self.page, headers={"Authorization": " ".join(args)})
else:
raise Exception('Missing parameters')
d1.addCallback(self.pageCallback)
dl = DeferredList([d1])
d1.addErrback(self.errorHandler)
dl.addCallback(self.listCallback)
def errorHandler(self,result):
# Bad thingy!
pass
def pageCallback(self, result):
########### I added this, to hold the data:
self.data = result
return result
def listCallback(self, result):
print result
# Added for effect:
if reactor.running:
reactor.stop()
a = GrabPage('http://google.com')
########### Just call it without assigning to data
#data = a.start() # Not the HTML
a.start()
########### I added this:
if not reactor.running:
reactor.run()
########### Reference the data attribute from the class
data = a.data
print '------REACTOR STOPPED------'
print
########### First 100 characters of a.data:
print '------a.data[:100]------'
print data[:100]

Categories

Resources