Source code for idaes.ui.log_monitor

##############################################################################
# Institute for the Design of Advanced Energy Systems Process Systems
# Engineering Framework (IDAES PSE Framework) Copyright (c) 2018, by the
# software owners: The Regents of the University of California, through
# Lawrence Berkeley National Laboratory,  National Technology & Engineering
# Solutions of Sandia, LLC, Carnegie Mellon University, West Virginia
# University Research Corporation, et al. All rights reserved.
# 
# Please see the files COPYRIGHT.txt and LICENSE.txt for full copyright and
# license information, respectively. Both files are also available online
# at the URL "https://github.com/IDAES/idaes".
##############################################################################
"""
Simple flask log viewer.  This is just a very prliminary start on a logging
viewer that can be used along side a jupyter notebook so that you can see what's
going on in your web browser without junking up the jupyter notbook with a bunch
of log messages. This could use a lot of work, but it's something.
"""
from __future__ import division, print_function, absolute_import
__author__ = "John Eslick"

import logging
import time
import datetime
import json
from flask import Flask, request

index_page = """<!DOCTYPE html>
<html>
<body>

<style>
.divformat {
    font-family: monospace;
    white-space: pre;
    font-size: 10pt;
}
</style>

<script>
setTimeout(timeoutFunction, 500);
var update_time_old = "0";

function log_update_listen() {
    var log_messages = document.getElementById('log_messages');
    log_messages.innerHTML = this.responseText;
    setTimeout(timeoutFunction, 500);
}

function update_listen() {
    var log_messages = document.getElementById('log_messages');
    if (this.responseText != update_time_old){
        update_time_old = this.responseText;
        var req = new XMLHttpRequest();
        var url = window.location.href + "log_entries"
        req.addEventListener("load", log_update_listen);
        req.open("GET", url);
        req.send();
    }
    else{
        setTimeout(timeoutFunction, 500);
    }
}

function timeoutFunction() {
    var req = new XMLHttpRequest();
    var url = window.location.href + "update"
    req.addEventListener("load", update_listen);
    req.open("GET", url);
    req.send();
}
</script>

<div id="log_messages" class="divformat"></div>

</body>
</html>
"""

app = Flask(__name__)

settings = {
    "max_display":80,
    "max_store":1000,
    "format":"%(asctime)s - %(levelname)s - %(name)s - %(message)s",
    "datefmt":"%Y-%m-%d %H:%M"}
log_entries = []
last_updated = "0"

[docs]@app.route("/") def index(): return index_page
[docs]@app.route("/log_entries") def lines_str(): global log_entries global settings fmt = logging.Formatter(fmt=settings["format"], datefmt=settings["datefmt"]) max_display = settings["max_display"] if len(log_entries) > max_display: return "\n".join( [fmt.format(entr) for entr in log_entries[-max_display:]]) else: return "\n".join( [fmt.format(entr) for entr in log_entries])
[docs]@app.route("/update") def get_last_updated(): return last_updated
[docs]@app.route('/settings', methods=['POST', 'GET']) def set_settings(): global settings if request.method == 'POST': try: settings["max_display"] = int(request.form["max_display"]) except: pass try: settings["max_store"] = int(request.form["max_store"]) except: pass try: settings["format"] = request.form["format"] except: pass try: settings["datefmt"] = request.form["datefmt"] except: pass return "" else: return json.dumps(settings)
[docs]@app.route('/log', methods=['POST']) def log(): global settings global last_updated global log_entries max_store = settings["max_store"] d = request.form.to_dict() # This part may be a bit sketchy but need to convert so repr of record # attrubutes back without eval and without adding much. Eval for this would # obviously be a bad idea securty wise. So far this handles # anything I've run into. The http handler just passed record.__dict__ # apperently using repr? on things like lists, tuples, None, ... # Really what I should do is impliment the mapLogRecord(record) in an # HTTPHandler subclass that uses json. d["created"] = json.loads(d["created"]) d["args"] = "[" + d["args"][1:] # args was a tuple, make it something json d["args"] = d["args"][:-1] + "]" # can handel d["args"] = tuple(json.loads(d["args"])) if d["exc_info"] == "None": d["exc_info"] = None if d["exc_text"] == "None": d["exc_text"] = None rec = logging.makeLogRecord(d) log_entries.append(rec) if len(log_entries) > max_store: log_entries = log_entries[-max_store:] last_updated = str(time.clock()) return ""