diff options
Diffstat (limited to 'PyArcWelder/py_logger.cpp')
-rw-r--r-- | PyArcWelder/py_logger.cpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/PyArcWelder/py_logger.cpp b/PyArcWelder/py_logger.cpp new file mode 100644 index 0000000..d05e059 --- /dev/null +++ b/PyArcWelder/py_logger.cpp @@ -0,0 +1,246 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Python Extension for the OctoPrint Arc Welder plugin. +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Copyright(C) 2020 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "py_logger.h" + +py_logger::py_logger(std::vector<std::string> names, std::vector<int> levels) : logger(names, levels) +{ + loggers_created_ = false; + check_log_levels_real_time = true; + py_logging_module = NULL; + py_logging_configurator_name = NULL; + py_logging_configurator = NULL; + py_arc_welder_gcode_conversion_logger = NULL; + gcode_conversion_log_level = 0; + py_info_function_name = NULL; + py_warn_function_name = NULL; + py_error_function_name = NULL; + py_debug_function_name = NULL; + py_verbose_function_name = NULL; + py_critical_function_name = NULL; + py_get_effective_level_function_name = NULL; +} +void py_logger::initialize_loggers() +{ + // Create all of the objects necessary for logging + // Import the arc_welder.log module + py_logging_module = PyImport_ImportModuleNoBlock("octoprint_arc_welder.log"); + if (py_logging_module == NULL) + { + PyErr_SetString(PyExc_ImportError, "Could not import module 'arc_welder.log'."); + return; + } + + // Get the logging configurator attribute string + py_logging_configurator_name = PyObject_GetAttrString(py_logging_module, "LoggingConfigurator"); + if (py_logging_configurator_name == NULL) + { + PyErr_SetString(PyExc_ImportError, "Could not acquire the LoggingConfigurator attribute string."); + return; + } + + // Create a logging configurator + PyGILState_STATE gstate = PyGILState_Ensure(); + std::cout << "Creating arguments for LoggingConfigurator creation.\r\n"; + PyObject* funcArgs = Py_BuildValue("(s,s,s)", "arc_welder", "arc_welder.", "octoprint_arc_welder."); + if (funcArgs == NULL) + { + std::cout << "Unable to create LoggingConfigurator arguments, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create LoggingConfigurator arguments."); + return; + } + std::cout << "Creating LoggingConfigurator..."; + py_logging_configurator = PyObject_CallObject(py_logging_configurator_name, funcArgs); + std::cout << "Complete.\r\n"; + Py_DECREF(funcArgs); + PyGILState_Release(gstate); + if (py_logging_configurator == NULL) + { + std::cout << "The LoggingConfigurator is null, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create a new instance of LoggingConfigurator."); + return; + } + + // Create the gcode_parser logging object + py_arc_welder_gcode_conversion_logger = PyObject_CallMethod(py_logging_configurator, (char*)"get_logger", (char*)"s", "octoprint_arc_welder.gcode_conversion"); + if (py_arc_welder_gcode_conversion_logger == NULL) + { + std::cout << "No child logger was created, exiting.\r\n"; + PyErr_SetString(PyExc_ImportError, "Could not create the arc_welder.gcode_parser child logger."); + return; + } + + // create the function name py objects + py_info_function_name = gcode_arc_converter::PyString_SafeFromString("info"); + py_warn_function_name = gcode_arc_converter::PyString_SafeFromString("warn"); + py_error_function_name = gcode_arc_converter::PyString_SafeFromString("error"); + py_debug_function_name = gcode_arc_converter::PyString_SafeFromString("debug"); + py_verbose_function_name = gcode_arc_converter::PyString_SafeFromString("verbose"); + py_critical_function_name = gcode_arc_converter::PyString_SafeFromString("critical"); + py_get_effective_level_function_name = gcode_arc_converter::PyString_SafeFromString("getEffectiveLevel"); + loggers_created_ = true; + std::cout << "Logger created successfully.\r\n"; + +} + +void py_logger::set_internal_log_levels(bool check_real_time) +{ + check_log_levels_real_time = check_real_time; + if (!check_log_levels_real_time) + { + + PyObject* py_gcode_conversion_log_level = PyObject_CallMethodObjArgs(py_arc_welder_gcode_conversion_logger, py_get_effective_level_function_name, NULL); + if (py_gcode_conversion_log_level == NULL) + { + PyErr_Print(); + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder - Could not retrieve the log level for the gcode parser logger."); + } + gcode_conversion_log_level = gcode_arc_converter::PyIntOrLong_AsLong(py_gcode_conversion_log_level); + + Py_XDECREF(py_gcode_conversion_log_level); + } +} + +void py_logger::log_exception(const int logger_type, const std::string& message) +{ + log(logger_type, ERROR, message, true); +} + +void py_logger::log(const int logger_type, const int log_level, const std::string& message) +{ + log(logger_type, log_level, message, false); +} + +void py_logger::log(const int logger_type, const int log_level, const std::string& message, bool is_exception) +{ + if (!loggers_created_) + return; + + // Get the appropriate logger + PyObject* py_logger; + long current_log_level = 0; + switch (logger_type) + { + case GCODE_CONVERSION: + py_logger = py_arc_welder_gcode_conversion_logger; + current_log_level = gcode_conversion_log_level; + break; + default: + std::cout << "Logging.arc_welder_log - unknown logger_type.\r\n"; + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder_log - unknown logger_type."); + return; + } + + if (!check_log_levels_real_time) + { + //std::cout << "Current Log Level: " << current_log_level << " requested:" << log_level; + // For speed we are going to check the log levels here before attempting to send any logging info to Python. + if (current_log_level > log_level) + { + return; + } + } + + PyObject* pyFunctionName = NULL; + + PyObject* error_type = NULL; + PyObject* error_value = NULL; + PyObject* error_traceback = NULL; + bool error_occurred = false; + if (is_exception) + { + // if an error has occurred, use the exception function to log the entire error + pyFunctionName = py_error_function_name; + if (PyErr_Occurred()) + { + error_occurred = true; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyErr_NormalizeException(&error_type, &error_value, &error_traceback); + } + } + else + { + switch (log_level) + { + case INFO: + pyFunctionName = py_info_function_name; + break; + case WARNING: + pyFunctionName = py_warn_function_name; + break; + case ERROR: + pyFunctionName = py_error_function_name; + break; + case DEBUG: + pyFunctionName = py_debug_function_name; + break; + case VERBOSE: + pyFunctionName = py_verbose_function_name; + break; + case CRITICAL: + pyFunctionName = py_critical_function_name; + break; + } + } + PyObject* pyMessage = gcode_arc_converter::PyUnicode_SafeFromString(message); + if (pyMessage == NULL) + { + std::cout << "Unable to convert the log message '" << message.c_str() << "' to a PyString/Unicode message.\r\n"; + PyErr_Format(PyExc_ValueError, + "Unable to convert the log message '%s' to a PyString/Unicode message.", message.c_str()); + return; + } + PyGILState_STATE state = PyGILState_Ensure(); + PyObject* ret_val = PyObject_CallMethodObjArgs(py_logger, pyFunctionName, pyMessage, NULL); + // We need to decref our message so that the GC can remove it. Maybe? + Py_DECREF(pyMessage); + PyGILState_Release(state); + if (ret_val == NULL) + { + if (!PyErr_Occurred()) + { + std::cout << "Logging.arc_welder_log - null was returned from the specified logger.\r\n"; + PyErr_SetString(PyExc_ValueError, "Logging.arc_welder_log - null was returned from the specified logger."); + } + else + { + std::cout << "Logging.arc_welder_log - null was returned from the specified logger and an error was detected.\r\n"; + // I'm not sure what else to do here since I can't log the error. I will print it + // so that it shows up in the console, but I can't log it, and there is no way to + // return an error. + PyErr_Print(); + PyErr_Clear(); + } + } + else + { + // Set the exception if we are doing exception logging. + if (is_exception) + { + if (error_occurred) + PyErr_Restore(error_type, error_value, error_traceback); + else + PyErr_SetString(PyExc_Exception, message.c_str()); + } + } + Py_XDECREF(ret_val); +} |