Google Cloud Logging malfunctioning from cloud function - python

I am currently trying to deploy a cloud function triggered by Pub/Sub written in Python. Previously, we used loguru to log. I am now making the switch to the cloud logging. I thought it would be rather simple but am quite puzzled. Here is the code I deployed in a Cloud Function, just to try logging :
import base64
import logging
import google.cloud.logging as google_logging
def hello_pubsub(event, context):
client = google_logging.Client()
client.setup_logging()
logging.debug("Starting function")
logging.info("Hello")
logging.warning("warning ! ")
pubsub_message = base64.b64decode(event['data']).decode('utf-8')
logging.info(pubsub_message)
logging.error("Exit function")
I followed the documentation I could find on the subject (but the pages can show various methods, and are not very clear). Here is the result on the Logging interface :
This is the result on the "Global" logs. Two questions here : why aren't the debug logs not shown, even if I explicitely set the log level as "debug" in the interface ? And why the logs are shown 1, 2 or 3 times, randomly ?
Now I try to display the logs for my Cloud Function only :
This is getting worse, now the logs are displayed up to 5 times (and not even the same number of times than in the "Global" tab), the levels of informations are all wrong (logging.info results in 1 info line, 1 error line ; error and warning results in 2 errors lines...)
I imagine I must be doing something bad, but I can't see what, as what I am trying to do is fairly simple. Can somebody please help me ? Thanks !
EDIT : I did the mistake of putting the initialization of the client in the function, this explains that the logs were displayed more than once. One problem left is that the warnings are displayed as errors in the "Cloud Function" tabs, and displayed correctly in the "Global" tab. Do someone has an idea about this ?

Try moving your setup outside of the function:
import base64
import logging
import google.cloud.logging as google_logging
client = google_logging.Client()
client.setup_logging()
def hello_pubsub(event, context):
logging.debug("Starting function")
logging.info("Hello")
logging.warning("warning ! ")
pubsub_message = base64.b64decode(event['data']).decode('utf-8')
logging.info(pubsub_message)
logging.error("Exit function")
As-is, you're adding new handlers on every request per instance.

You should use the Integration with Python logging module¶
import logging
import base64
import google.cloud.logging # Don't conflict with standard logging
from google.cloud.logging.handlers import CloudLoggingHandler
client = google.cloud.logging.Client()
handler = CloudLoggingHandler(client)
cloud_logger = logging.getLogger('cloudLogger')
cloud_logger.setLevel(logging.INFO) # defaults to WARN
cloud_logger.addHandler(handler)
def hello_pubsub(event, context):
import logging
cloud_logger.debug("Starting function")
cloud_logger.info("Hello")
cloud_logger.warning("warning ! ")
pubsub_message = base64.b64decode(event['data']).decode('utf-8')
cloud_logger.info(pubsub_message)
cloud_logger.error("Exit function")
return 'OK', 200

Related

Trouble deploying telegram bot on Render

I am trying to deploy this telegram bot on Render, but the deploy always fails (although on the first minutes after I receive the build successful log, it works perfectly).
However, minutes later the deploy fails and I keep getting this error log:
telebot.apihelper.ApiTelegramException: A request to the Telegram API was unsuccessful. Error code: 409. Description: Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
Here is the "bot.py" script I coded. Suppressed some non important parts of the code. Also, all "src." modules were created by me for better code organization. There are no telebot methods used on those modules.
import pandas as pd
from datetime import datetime, date
import src.visualization.visualize as vz
import src.auxiliary.auxiliary as aux
import src.data.wrangling as w
import src.data.database as db
import os
import telebot
API = os.getenv("API_KEY")
bot = telebot.TeleBot(API)
#bot.message_handler(commands=["IBOVESPA", "SP500", "NASDAQ"])
def responder(mensagem):
# generic code and calculations suppressed
if condition:
# telebot method used
bot.send_photo(
mensagem.chat.id,
photo = photo
)
else:
# more generic code suppressed
# telebot method used
bot.send_message(
mensagem.chat.id,
aux.create_answer(assets)
)
# telebot method used
bot.send_photo(
mensagem.chat.id,
photo = photo
)
def verificar(mensagem):
return True
def calculating_msg(mensagem, date):
# telebot method used
bot.send_message(
mensagem.chat.id,
“generic answer”
)
#bot.message_handler(func=verificar)
def responder(mensagem):
text= (
“standard reply to any message”
)
# telebot method used
bot.reply_to(mensagem, text)
# telebot method used
bot.polling()
Already revoked an old api_key on telegram and used a fresh-generated one and nothing changes.
Edit: works perfectly running on local. Only receive this warning:
\src\visualization\visualize.py:34: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail.
fig = plt.figure(figsize=(15, 8))
WARNING: QApplication was not created in the main() thread.

Azure app insights not working with Python console app

I am trying to setup AI similar to how it is being done here
I am using Python 3.9.13 and following packages: opencensus==0.11.0, opencensus-ext-azure==1.1.7, opencensus-context==0.1.3
My code looks something like this:
import logging
import time
from opencensus.ext.azure.log_exporter import AzureLogHandler
# create the logger
app_insights_logger = logging.getLogger(__name__)
# set the handler
app_insights_logger.addHandler(AzureLogHandler(
connection_string='InstrumentationKey=00000000-0000-0000-0000-000000000000')
)
# set the logging level
app_insights_logger.setLevel(logging.INFO)
# this prints 'logging level = 20'
print('logging level = ',app_insights_logger.getEffectiveLevel())
# try to log an exception
try:
result = 1 / 0
except Exception:
app_insights_logger.exception('Captured a math exception.')
app_insights_logger.handlers[0].flush()
time.sleep(5)
However the exception does not get logged, I tried adding the explicit flush as mentioned in this post
Additionally, I tried adding the instrumentation key as mentioned in the docs, when that didn't work I tried with the entire connection string(the one with the ingestion key)
So,
How can I debug if my app is indeed sending requests to Azure ?
How can I check on the Azure portal if it is a permission issue ?
You can set the severity level before logging any kind of telemetry information to Application Insights.
Note: By default, root logger can be configured with warning severity. if you want to add other severity information you have to set like (logger.setLevel(logging.INFO)).
I am using below code to log the telemetry information in Application Insights
import logging
from logging import Logger
from opencensus.ext.azure.log_exporter import AzureLogHandler
AI_conn_string= '<Your AI Connection string>'
handler = AzureLogHandler(connection_string=AI_conn_string)
logger = logging.getLogger()
logger.addHandler(handler)
#by default root logger can be configured with warning severity.
logger.warning('python console app warning log in AI ')
# setting severity for information level logging.
logger.setLevel(logging.INFO)
logger.info('Test Information log')
logger.info('python console app information log in AI')
try:
logger.warning('python console app Try block warning log in AI')
result = 1 / 0
except Exception:
logger.setLevel(logging.ERROR)
logger.exception('python console app error log in AI')
Results
Warning and Information log
Error log in AI
How can I debug if my app is indeed sending requests to Azure ?
We cannot debug the information whether the telemetry data send to Application Insights or not. But we can see the process like below
How can I check on the Azure portal if it is a permission issue ?
Instrumentation key and connection string has the permission to access the Application Insights resource.

Tornado substituting custom logger on server (not on local computer)

I have a tornado application and a custom logger method. My code to build and use the custom logger is the following:
def create_logger():
"""
This function creates the logger functionality to be used throughout the Python application
:return: bool - true if successful
"""
# Configuring the logger
filename = "PythonLogger.log"
# Change the current working directory to the logs folder, so that the logs files is written in it.
os.chdir(os.path.normpath(os.path.normpath(os.path.dirname(os.path.abspath(__file__)) + os.sep + os.pardir + os.sep + os.pardir + os.sep + 'logs')))
# Create the logs file
logging.basicConfig(filename=filename, format='%(asctime)s %(message)s', filemode='w')
# Creating the logger
logger = logging.getLogger()
# Setting the threshold of logger to DEBUG
logger.setLevel(logging.NOTSET)
logger.log(0, 'El logger está inicializado')
return True
def log_info_message(msg):
"""
Utility for message logging with code 20
:param msg:
:return:
"""
return logging.getLogger().log(20, msg)
In the code, I initialize the logger and already write a message to it before the Tornado application initialization:
if __name__ == '__main__':
# Logger initialization
create_logger()
# First log message
log_info_message('Initiating Python application')
# Starting Tornado
tornado.options.parse_command_line()
# Specifying what app exactly is being started
server = tornado.httpserver.HTTPServer(test.app)
server.listen(options.port)
try:
if 'Windows_NT' not in os.environ.values():
server.start(0)
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
tornado.ioloop.IOLoop.instance().stop()
Then let's say my method get of HTTP request is as follows (only interesting lines):
class API(tornado.web.RequestHandler):
def get(self):
self.write('Get request ')
logging.getLogger("tornado.access").log(20, 'Hola')
logging.getLogger("tornado.application").log(20, '1')
logging.getLogger("tornado.general").log(20, '2')
log_info_message('Received a GET request at: ' + datetime.datetime.now().strftime("%d-%b-%Y (%H:%M:%S.%f)"))
What I see is a difference between local testing and testing on server.
A) On local, I can see log message at first script running, and log messages of requests (after initializing Tornado app) in my log file and the Tornado logs.
B) On server, I only see the first message, not my log messages when Get requests are accepted and also see Tornado's loggers when there's an error, but even don't see the messages produced by Tornado's loggers. I guess that means that somehow Tornado is re-initializing the logger and making mine and his 3 ones write in some other file (somehow that does not affect when errors happens??).
I am aware that Tornado uses its own 3 logging functions, but somehow I would like to use mine as well, at the same time as keeping the Tornado's ones and writing them all into the same file. Basically reproduce that local behaviour on server but also keeping it when some error happens, of course.
How could I achieve this?
Thanks in advance!
P.S.: if I add a name to the logger, let's say logging.getLogger('Example') and changed log_info_message function to return logging.getLogger('Example').log(20, msg), Tornado's logger would fail and raise error. So that option destroys its own loggers...
It seems the only problem was that, on the server side, tornado was setting the the mininum level for a log message to be written on log file higher (minimum of 40 was required). So that logging.getLogger().log(20, msg) would not write on the logging file but logging.getLogger().log(40, msg) would.
I would like to understand why, so if anybody knows, your knowledge would be more than welcome. For the time being that solution is working though.
tornado.log defines options that can be used to customise logging via command line (check tornado.options) - one of them is logging that defines the log level used. You are likely using this on the server and setting it to error.
When debugging logging I suggest you create a RequestHandler that will log or return the structure of the existing loggers by inspecting the root logger. When you see the structure it is much easier to understand why it works the way it works.

Duplicated log entries in Google App Engine (Python 3)

I'm struggling to find out why my log entries are being duplicated in Cloud Logging.
I use a custom dummy handler that does nothing, and I'm also using a named logger.
Here's my code:
import google.cloud.logging
import logging
class MyLogHandler(logging.StreamHandler):
def emit(self, record):
pass
# Setting Up App Engine's Logging
client = google.cloud.logging.Client()
client.get_default_handler()
client.setup_logging()
# Setting Up my custom logger/handler
my_handler = MyLogHandler()
logging.getLogger('my_logger').addHandler(my_handler)
logging.getLogger('my_logger').setLevel(logging.DEBUG)
logging.getLogger('my_logger').debug('Why this message is being duplicated?') # please note that i'm logging into 'my_logger' logger, I'm not using root logger for this message
In the first place, I think this message shouldn't even show in Cloud Logging because i'm using a named logger called 'my_logger' and cloud logging is attached to root logger only, but anyway...
The above code is imported into my app.py which bootstraps a Flask app on app engine.
Here is a screenshot of the issue:
This guy has a similar issue: Duplicate log entries with Google Cloud Stackdriver logging of Python code on Kubernetes Engine
But I tried every workaround suggested in that topic and didn't work either.
Is there something I'm missing here? Thanks in advance.
from google.cloud.logging.handlers import AppEngineHandler
root_logger = logging.getLogger()
# use the GCP appengine handlers only in order to prevent logs from getting written to STDERR
root_logger.handlers = [handler for handler in root_logger.handlers
if isinstance(handler, AppEngineHandler)]

Why I can't connect my telegram bot to Mongodb Atlas using Python?

This is my code
from telegram.ext import Updater, CommandHandler
import os
from pymongo import MongoClient
TOKEN = 'TOKEN'
def get_db(update, context):
cluster = MongoClient("mongodb+srv://testing:12345678#cluster0.gs9k5.mongodb.net/test?retryWrites=true&w=majority")
result = list(cluster.get_database('DBNAME')['COLLECTIONNAME'].find({}))
update.message.reply_text(str(result))
def main():
updater = Updater(TOKEN, use_context=True)
dp = updater.dispatcher
dp.add_handler(CommandHandler("getdb", get_db))
updater.start_webhook(listen="#.#.#.#",
port=int(PORT),
url_path=TOKEN)
updater.bot.setWebhook('https://MYHEROKUAPP.herokuapp.com/' + TOKEN)
updater.idle()
if __name__ == '__main__':
main()
Everytime I type /getdb, the bot doesn't give me any response. When I tried several experiments, Seems there's some Error on cluster variable. I used try except syntax, but the bot didn't show anything, even from the except and I couldn't found the Error name as well. And I'm using heroku server for the telegram bot. How to fix this?
You can connect to default db (which is the one defined in the connection string) and query the collections like this
client = MongoClient('connect-string')
db = client.get_default_database()
# 'collection_name' is the name of the Mongo collection
list = db.collection_name.find()
I'm not sure if you still have this issue, but the code seems to be okay so far.
Try logging the app info to the terminal to get a better idea of what the error is.
import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
It may be that your MongoDB Atlas is not allowing connection from the server you are running your code from. You can check your cluster to see if it allows access to the DB from your server. You can add the IP address of your server to the cluster to allow it succesfully access the data.
If the collection you're trying to display all items from is large, Telegram will throw an error because the ensuing message will be too long. Ensure that you're running your test with only a few items in your test database.
You should be ale to check your heroku logs or in your terminal to see what other errors might be happening

Categories