Permission error when initialising RotatingFileHandler logging in Flask app factory - python

There are a lot of similar questions on SO, but I couldn't one that exactly fit my situation.
I'm setting up logging in my app factory like so:
__init__.py
import os
from flask import Flask
from logging.config import dictConfig
LOG_FOLDER = f'{os.path.dirname(os.path.abspath(__file__))}/logs'
def create_app(test_config=None):
# Setup logging
# Make log folder if it doesn't exist
try:
os.makedirs(LOG_FOLDER)
print("created logs folder")
except OSError:
print("log folder already exists")
pass
dictConfig({
"version": 1,
"handlers": {
"fileHandler": {
"class": "logging.handlers.RotatingFileHandler",
"formatter": "myFormatter",
"filename": f"{LOG_FOLDER}/flask.log",
"maxBytes": 500,
"backupCount": 5
},
"werkzeugFileHandler": {
"class": "logging.handlers.RotatingFileHandler",
"formatter": "myFormatter",
"filename": f"{LOG_FOLDER}/werkzeug.log",
"maxBytes": 500,
"backupCount": 5
},
"console": {
"class": "logging.StreamHandler",
"formatter": "myFormatter"
}
},
"loggers": {
APP_NAME: {
"handlers": ["fileHandler", "console"],
"level": "INFO",
},
"werkzeug": {
"level": "INFO",
"handlers": ["werkzeugFileHandler", "console"],
}
},
"formatters": {
"myFormatter": {
"format": "[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s"
}
}
})
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
<remainder omitted>
And accessing the logger in my other classes like so:
foo.py
from flask import Flask
from definitions import APP_NAME
app = Flask(APP_NAME)
app.logger.info("blah")
But when it comes time for RotatingFileHandler to rename flask.log to flask.log.1, I get this error I've seen in numerous SO posts:
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\user\\project_root\\logs\\flask.log' -> 'C:\\Users\\user\\project_root\\logs\\flask.log.1'
I am running the flask server locally in development mode, using the flask run cli command.
Another thing to note is, when the flask server is running, I am unable to modify (i.e. delete or rename) the log files manually, so it seems the mere act of having the server running is locking the files from modification? Is it wrong to initialise the logging in __init__.py, or is there something I'm missing?

I think this is a duplicated question as PermissionError when using python 3.3.4 and RotatingFileHandler. But just reposting my answer:
Spent half a day on this as non previous answer resolved my issue.
My working solution is to use https://pypi.org/project/concurrent-log-handler/ instead of RotatingFileHandler. In multiple thread scenarios like Flask app, PermissionError will be raised when we rotate the log file that reaches maximum size.
Install pypiwin32 to get rid of No Module name win32con error.
Thanks go to https://www.programmersought.com/article/43941158027/

Try changing the delay parameter to True in your handler (in my case I used TimeRotatingFileHandler).
https://stackoverflow.com/a/69378029/15036810

Related

FLASK : plotly : Error: module 'index' has no attribute 'app.server'

the context
I am debugging an application using Visual Studio Code (VSCode).
The application relies mainly on https://plot.ly, https://palletsprojects.com/p/flask, https://pandas.pydata.org/ and https://numpy.org/
Breakpoints ARE NOT hit!
The breakpoints ARE NOT hit when I am using the launch.json (See [1])
I can debug with this launch.json (See [2]) but the debugger does not stops at the breakpoint !
I would like VSCode to stop on my breakpoints when necessary
**What is the correct configuration for launch.json to hit the breakpoints? **
Thank you for the time you are investing helping me!
the hierarchy of the project
launch.json
index.py See [4]
app.py See [3]
pages
index.py
transactions.py
launch.json is described here below [1]
the issue : Error: module 'index' has no attribute 'app.server'
The Error message is displayed after clicking on 'Start debugging > F5' = Error: module 'index' has no attribute 'app.server'
I tried dozens of ways to set the "FLASK_APP": "index:app.server" but they generate diverse error messages :
"FLASK_APP": "index:app.server" generates this error Error: A valid Flask application was not obtained from "index:app".
"FLASK_APP": "index.py" generates this error Error: Failed to find Flask application or factory in module "index". Use "FLASK_APP=index:name to specify one.
for information : gunicorn command (working)
here is a command available in azure-pipelines.yml running the plotly app :
gunicorn --bind=0.0.0.0 --timeout 600 index:app.server
attached files
[1] launch.json - non working
{
"version": "0.2.0",
"configurations": [
{
"name": "Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "index:app.server",
"FLASK_ENV": "development",
"FLASK_DEBUG": "1",
"FLASK_RUN_PORT": "8052"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
}
]
}
[2] launch.json - working but breakpoints are not hit
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${workspaceRoot}\\index.py",
"console": "integratedTerminal"
}
]
}
[3] webapp.py
# -*- coding: utf-8 -*-
import dash
app = dash.Dash(
__name__, meta_tags=[{"name": "viewport",
"content": "width=device-width, initial-scale=1"}]
)
server = app.server
app.config.suppress_callback_exceptions = True
index.py - root of the application
# -*- coding: utf-8 -*-
import dash_html_components as html
import dash_core_components as dcc
from webapp import app
from dash.dependencies import Input, Output
from pages import (
transactions, index)
# Describe the layout/ UI of the app
app.layout = html.Div([
dcc.Location(id="url", refresh=False),
html.Div(id="page-content")
])
# Update page
#app.callback(Output("page-content", "children"),
[Input("url", "pathname")])
def display_page(pathname):
if pathname == "/dash/index":
return index.layout
if pathname == "/dash/transactions":
return transactions.layout
else:
return index.layout
if __name__ == "__main__":
app.run_server(debug=True, port=8051)
Your [1] example isn't working because you set FLASK_APP to index:app.server which tries to find an attribute named app.server on the index module. Attribute names can't have a dot (you can verify this by importing that module and trying out getattr(index, "app.server")). You should be able to make FLASK_APP simply say index to have it work.
See the Flask documentation on app discovery for more details.

How to debug Django custom management command using VS Code

I am trying to debug a custom management command using Visual Studio Code.
For this I have gone through the official VS Code tutorials for working with Python and Django, and I have managed to get debugging to work while following these tutorials.
VS Code Python tutorial /
VS Code Django tutorial
The problem is that for a Python script (no Django), the debugger works because I run a specific file (by pressing f5) while the file's tab is open. Django debugging works because VS Code knows when a browser request causes my Django app to hit a break-point I entered in VS Code.
But a custom management command is run differently. For this I type the following in the console:
python manage.py name_of_management_command
How do I debug this in VS Code?
While writing this question I came up with a solution myself.
In the VS Code launch.json file (which contains the settings for the VS Code Django debugger) contains the following entry, by default:
"args": ["runserver", "--noreload", "--nothreading"]
I changed this to:
"args": ["name_of_management_command"]
Then start the debugger (press f5), and I am debugging my custom management command
Rik's answer is correct, but requires changing the launch config for every management command, or creating multiple launch config entries.
To create one entry that can debug all management commands, even the ones you still have to write, add the following config to your launch.json:
{
"name": "Django MGMT Command",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"${fileBasenameNoExtension}"
]
}
This works because the name of the management command is the name of the file in which it's defined, without the .py extension. Or ${fileBasenameNoExtension} for short.
See https://code.visualstudio.com/docs/editor/variables-reference for other variables you can use in your launch config.
#Rik and #jrial are missing an important argument, which is justMyCode, suppose you want to do a custom command that use startprojectapp and you want to see the code behind it to understand how to pass the arguments. You need to specify that argument for being able to enter there.
Also, I prefer to select the custom command from a list, instead of having to create one configuration to each file (#Rik solution), or require having the file with focus (#jrial solution).
A complete configuration can be found here.
Configuration to debug any command added to option list:
{
"name": "Django Command",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"${input:variableID}",
"restaurants"
],
"justMyCode": false
}
],
"inputs": [{
"id": "variableID",
"description": "Select client or server",
"type": "pickString",
"options": ["createsuperuser", "startapp", "A CUSTOM COMMAND"],
"default": "createsuperuser"
}]
Configuration to debug the open command file: (#jrial solution modified)
{
"name": "Django Current Custom Command",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"${fileBasenameNoExtension}"
],
"justMyCode": false
}
if you don't want to go through third party libraries code, omit the variable.
Configuration to debug the open specific custom_command.py file:(#Rik solution modified)
{
"name": "Django Current Custom Command",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/manage.py",
"args": [
"custom_command",
"THIS CAN BE A REQUIRED ARGUMENT",
],
"justMyCode": false
}

Django - Print only errors when running all tests

We're using python - Django 1.10. We have more than 1000 tests. When running all the tests we're getting tons of logs to stdout.
It mainly hurts on deployments - we're creating a docker instance and run all our tests (with python manage.py test).
I would like to somehow print only errors when running all tests.
Is there a way to do such thing?
Perhaps create a test specific test_settings.py that overrides the log level with ERROR when the tests are run.
For example, if the main settings.py contains:
LOGGING = {
...
'loggers': {
'myapp': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
}
}
}
Then you could create a test_settings.py that overrides the log level.
from settings import *
LOGGING['loggers']['myapp']['level'] = 'ERROR'
And then specify the test_settings when you run your tests.
python manage.py test --settings test_settings

PermissionError when using python 3.3.4 and RotatingFileHandler

I am trying to get a rotating log file for a GUI application I am writing with python 3.3.4 and PyQt4.
I have the following snippet of code in my main script:
import logging
import resources
logger = logging.getLogger('main.test')
def main():
logger.setLevel(logging.DEBUG)
fh = RotatingFileHandler(resources.LOG_FILE_PATH, maxBytes=500, backupCount=5)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.info('main')
I have the maxBytes low so that I can test the rotating is working correctly, which it is not. I am getting the following error whenever the log should be rotated:
Traceback (most recent call last):
File "C:\Python33\lib\logging\handlers.py", line 73, in emit
self.doRollover()
File "C:\Python33\lib\logging\handlers.py", line 176, in doRollover
self.rotate(self.baseFilename, dfn)
File "C:\Python33\lib\logging\handlers.py", line 116, in rotate
os.rename(source, dest)
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\myuser\\.logtest\\test.log.1'
And nothing is logged. Any help is much appreciated.
Thank you
Spent half a day on this as non previous answer resolved my issue.
My working solution is to use https://pypi.org/project/concurrent-log-handler/ instead of RotatingFileHandler. In multiple thread scenarios like Flask app, PermissionError will be raised when we rotate the log file that reaches maximum size.
Install pypiwin32 to get rid of No Module name win32con error.
Thanks go to https://www.programmersought.com/article/43941158027/
Instead of adding handler to the logger object you can directly specify handler in basicConfig(). If you add RotatingFileHandler to the logger object then one object might open the log file and another at the same time might try to rename it which throws the PermissionError.
Below code seems to work pretty well.
import logging
import resources
from logging.handlers import RotatingFileHandler
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[RotatingFileHandler(filename=resources.LOG_FILE_PATH, maxBytes=500, backupCount=5)])
logger = logging.getLogger('main.test')
def main():
logger.setLevel(logging.DEBUG)
logger.info('main')
In my case it happens only in Windows. To solve it I changed the delay parameter to True for my TimedRotatingFileHandler log handler.
Docs -> https://docs.python.org/3/library/logging.handlers.html#logging.handlers.TimedRotatingFileHandler
You cannot specify the same filename in both basicConfig() and RotatingFileHandler(). I had this same issue and I removed the filename parameter from basicConfig() and it now works.
In my case (Windows Server 2016 + IIS + FastCGI + Flask) finally I fix it by turning off files indexing in the folder.
how-to
Source:
https://stackoverflow.com/a/22467917/9199668
Btw, it was working for months correctly... I have no idea why...
Check that the file isn't being kept open by e.g. Windows file indexing, anti-virus or other software. Files that are open can't be renamed.
I changed the application to use dictConfig and created a separate file that holds the dictionary configuration. At the top of my main application I have:
from log.logger import LOGGING
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('testlogging')
Then in log.logger I have:
import logging
import sys
import resources
LOGGING = {
"version":1,
"handlers":{
"fileHandler":{
"class":"logging.handlers.RotatingFileHandler",
"formatter":"myFormatter",
"filename":resources.LOG_FILE_PATH,
"maxBytes":100000,
"backupCount":5
},
"console":{
"class":"logging.StreamHandler",
"formatter":"myFormatter"
}
},
"loggers":{
"aoconnect":{
"handlers":["fileHandler", "console"],
"level":"DEBUG",
}
},
"formatters":{
"myFormatter":{
"format":"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
}
}
This all seems to work pretty well.
In My case file size is full, after removing server.log file it worked
LOGS_DIR = os.path.join(BASE_DIR, 'logs')
LOGGING = {
'version': 1,
'handlers': {
'log_file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(LOGS_DIR, 'server.log'),
'backupCount': 10, # keep at most 10 log files
'maxBytes': 5*1024*1024 # 5242880 bytes (5MB)
},
},
'loggers': {
'django': {
'handlers':['log_file'],
'propagate': True,
'level':'INFO',
},
},
}

Replace string in Python and open browser with result

I am trying to build a plugin for the program Sublimetext2.
It uses plugins coded with Python. I have no Python knowledge at all but from looking at existing plugins and my PHP knowledge here is what I need help with...
this is the start of the Python file so far
import sublime, sublime_plugin
import webbrowser
settings = sublime.load_settings('openonserver.sublime-settings')
settings.get('file_path_prefix')
settings.get('server_url')
class OpenonServerCommand(sublime_plugin.TextCommand):
def run(self,edit):
file_path = self.view.file_name()
What I need to do though take the value of the settings
file_path will be the path to the file I am running this on so lets say...
E:\Server\htdocs\mytest_project_\some\folder_\test.php
The settings
file_path_prefix will be E:\Server\htdocs\ and
server_url will be http://localhost/
I need to see if file_path_prefix exist in file_path if it does,
I need to replace the E:\Server\htdocs\ with the http://localhost/ and replace all \ to / and then store this new path in a variable
so...
E:\Server\htdocs\mytest_project_\some\folder_\test.php would become
http://localhost/mytest_project_/some/folder_/test.php
I then need to send this to the browser.
Any help is greatly appreciated
Use
os.system("path_to_browser url")
To run any external program. I also recomend to take a look at this comment
Ok after many hours (I hate Python now) my solution (i'm very not impressed) but it partially works
#Context.sublime-menu
[
{ "command": "openserver", "caption": "Open on Server" }
]
#Default (Windows).sublime-keymap
[
{ "keys": ["ctrl+shift+b"], "command": "openserver" }
]
#Main.sublime-menu
[
{
"caption": "Tools",
"mnemonic": "T",
"id": "tools",
"children":
[
{ "command": "openserver", "caption": "Open on Server" }
]
}
]
#Openserver.sublime-commands
[
{
"caption": "Open file on Server in Browser",
"command": "openserver"
}
]
#Openserver.sublime-settings
{
"file_path_prefix": "E:/Server/htdocs",
"url_prefix": "http://localhost"
}
Main file
#openserver.py
import sublime, sublime_plugin
import os
import webbrowser
import re
import os2emxpath
import logging
import sys
class OpenserverCommand(sublime_plugin.TextCommand):
def run(self,edit):
file_path = self.view.file_name()
settings = sublime.load_settings('Openserver.sublime-settings')
file = os2emxpath.normpath(file_path)
url = re.sub(settings.get('file_path_prefix'), settings.get('url_prefix'), file)
#logging.warning(url)
#webbrowser.open_new(url)
if sys.platform=='win32':
os.startfile(url)
elif sys.platform=='darwin':
subprocess.Popen(['open', url])
else:
try:
subprocess.Popen(['xdg-open', url])
except OSError:
logging.warning(url)
Now when I say it works but it partially doesn't, it does take the file name, replaces my path and server URL from a settings file and then does launch a browser with the proper URL
Except, in Sublimetext2 when you run this on a .py file or any file that you do not have set to be able to open in a web browser, then instead of opening the file in the web browser it will give the windows popup asking to set a default program to open the file, very annoying!

Categories