Skip to content

Instantly share code, notes, and snippets.

@ZAYDEK ZAYDEK/howto
Last active Jul 21, 2019

Embed
What would you like to do?
Proof-of-concept for SQL-driven logging. You can like this tweet for updates: https://twitter.com/username_ZAYDEK/status/1114827120524419073
bash$ pg_ctl -D /usr/local/var/postgres start ## init postgres
bash$ psql -d postgres ## connect to postgres
psql# create database logger; -- create a database named "logger"
psql# begin; -- start a transaction
psql# \i log.sql -- load database
psql# commit; -- commit the transaction
bash$ go run log.go ## run driver program
package main
import (
"database/sql"
"encoding/json"
"errors"
"reflect"
"runtime"
_ "github.com/lib/pq"
)
/*
* constants
*/
const SERVICE_NAME = "log"
type Level string
const (
INFO = "info"
WARN = "warn"
FATA = "fata"
)
/*
* JSON
*/
var ErrServerError = JSON{"status_code": 500, "status_desc": "server error"}
type JSON map[string]interface{}
func (j JSON) String() string {
data, err := json.MarshalIndent(j, "", "\t")
if err != nil {
str := ErrServerError.String()
return str
}
return string(data)
}
/*
* logger
*/
// stackoverflow.com/questions/7052693
func fn_name(x interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(x).Pointer()).Name()
}
func LogStdout(fn_ptr interface{}, fn_values JSON) {
_, db_err := db.Exec(`select log_stdout($1, $2, $3)`,
SERVICE_NAME, fn_name(fn_ptr), fn_values.String())
must(db_err) // just for testing
}
func LogStderr(level string, fn_ptr interface{}, fn_values JSON, err error) {
_, db_err := db.Exec(`select log_stderr($1, $2, $3, $4, $5)`,
SERVICE_NAME, level, fn_name(fn_ptr), fn_values.String(), err.Error())
must(db_err) // just for testing
}
/*
* example_fns
*/
func example_fn1(a, b int, c string) {
LogStdout(example_fn1, JSON{"a": a, "b": b, "c": c})
}
func example_fn2(a, b int, c string) {
err := errors.New("something went wrong")
LogStderr(INFO, example_fn2, JSON{"a": a, "b": b, "c": c}, err)
}
/*
* main
*/
var db *sql.DB
func must(err error) {
if err != nil {
panic(err)
}
}
func must_open(conn_str string) *sql.DB {
postgres, err := sql.Open("postgres", conn_str)
must(err)
err = postgres.Ping()
must(err)
return postgres
}
func main() {
db = must_open("postgres://zaydek@localhost/logger?sslmode=disable")
defer db.Close()
example_fn1(1, 2, "hello, world!")
example_fn2(1, 2, "hello, world!")
}
create extension pgcrypto;
/*
* helpers
*/
create table levels (level text primary key);
insert into levels (level) values ('info'), ('warn'), ('fata');
/*
* tables
*/
create table stdout (
id text primary key default replace(gen_random_uuid()::text, '-', '') unique,
timestamp timestamptz not null default now(),
service_name text not null,
fn_name text not null,
fn_values jsonb);
create table stderr (
id text primary key default replace(gen_random_uuid()::text, '-', '') unique,
timestamp timestamptz not null default now(),
service_name text not null,
level text not null references levels (level),
fn_name text not null,
fn_values jsonb,
err text);
/*
* functions
*/
create function log_stdout(_service_name text, _fn_name text, _fn_values jsonb, out _id text) as $$
begin
insert into stdout (service_name, fn_name, fn_values)
values (_service_name, _fn_name, _fn_values)
returning id into _id;
end;
$$ language plpgsql;
create function log_stderr(_service_name text, _level text, _fn_name text, _fn_values jsonb, _err text, out _id text) as $$
begin
insert into stderr (service_name, level, fn_name, fn_values, err)
values (_service_name, _level, _fn_name, _fn_values, _err)
returning id into _id;
end;
$$ language plpgsql;
/*
* views
*/
create view stdout_short as select substr(id, 1, 4) as id, timestamp::timestamp(0), service_name, fn_name, fn_values from stdout;
create view stderr_short as select substr(id, 1, 4) as id, timestamp::timestamp(0), service_name, level, fn_name, fn_values, err from stderr;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.