-
Notifications
You must be signed in to change notification settings - Fork 1
feat(telemetry server): create Go Telemetry server and update cli to env #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c6291f6
3eae1d2
d831892
ae66528
7188b88
68e3db1
3a09a37
92a6663
4ae08bf
d461ee7
8d430cc
bc190f4
3e4e4a7
ec9ffda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
name: CI/CD | ||
|
||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: ["**"] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
# Setup Go | ||
- name: Setup Go | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
|
||
- name: Build | ||
run: | | ||
mkdir -p bin | ||
go build -o bin/pyrevit-telemetryserver | ||
- name: Upload Artifacts | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: bin | ||
path: bin | ||
|
||
- name: Run Tests | ||
run: go test -v ./... | ||
Comment on lines
+31
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test should be run before the build to ensure everything is OK |
||
|
||
docker: | ||
runs-on: ubuntu-latest | ||
needs: build | ||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up QEMU | ||
uses: docker/setup-qemu-action@v3 | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Login to DockerHub # TODO: uncomment if pushing | ||
# uses: docker/login-action@v3 | ||
# with: | ||
# username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
# password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
|
||
- name: Build Docker image | ||
run: docker build -t pyrevit-telemetryserver:latest . | ||
|
||
# - name: Push Docker image # Will be uncommented if pushing | ||
# run: docker push pyrevit-telemetryserver:latest | ||
Comment on lines
+34
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This job should run only on "release" and sync the docker tag with the version. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,56 @@ | ||
# telemetry-server | ||
Telemetry Server for pyRevit | ||
# pyRevit Telemetry Server | ||
|
||
## Quick Start | ||
|
||
```sh | ||
# Build the server | ||
go build -o pyrevit-telemetryserver | ||
|
||
# Run with environment variables | ||
PYREVT_TELEMETRY_DB_CONNSTRING="mongodb://localhost:27017/atoma-test" \ | ||
PYREVT_TELEMETRY_SCRIPTS_TABLE="scripts" \ | ||
PYREVT_TELEMETRY_EVENTS_TABLE="events" \ | ||
./pyrevit-telemetryserver --port=8080 | ||
``` | ||
|
||
## Environment Variables | ||
|
||
| Variable | Description | Example | | ||
|----------------------------------------|---------------------------------------------|-----------------------------------------| | ||
| PYREVT_TELEMETRY_DB_BACKEND | Database backend (`mongo`, `postgres`, etc) | mongo | | ||
| PYREVT_TELEMETRY_DB_CONNSTRING | Database connection string | mongodb://localhost:27017/atoma-test | | ||
| PYREVT_TELEMETRY_SCRIPTS_TABLE | Name of the scripts table/collection | scripts | | ||
| PYREVT_TELEMETRY_EVENTS_TABLE | Name of the events table/collection | events | | ||
| PYREVT_TELEMETRY_PORT | Port to run the server on | 8080 | | ||
| PYREVT_TELEMETRY_DEBUG | Enable debug logging (`true`/`false`) | true | | ||
| PYREVT_TELEMETRY_TRACE | Enable trace logging (`true`/`false`) | false | | ||
|
||
> All CLI options can be set via environment variables. CLI flags override environment variables. | ||
|
||
## API Endpoints | ||
|
||
- `GET /api/v1/status` — Health check | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is also the /api/v2/status endpoint |
||
- `GET /api/v1/scripts/` — List v1 script telemetry | ||
- `POST /api/v1/scripts/` — Submit v1 script telemetry | ||
- `GET /api/v2/scripts/` — List v2 script telemetry | ||
- `POST /api/v2/scripts/` — Submit v2 script telemetry | ||
- `GET /api/v2/events/` — List v2 event telemetry | ||
- `POST /api/v2/events/` — Submit v2 event telemetry | ||
|
||
## Example: Submit Script Telemetry | ||
|
||
```sh | ||
curl -X POST -H "Content-Type: application/json" \ | ||
-d '{"date":"2024-03-30","time":"08:45:00", ... }' \ | ||
http://localhost:8080/api/v1/scripts/ | ||
``` | ||
|
||
## Docker Usage | ||
|
||
### Build and Run with Docker Compose | ||
|
||
```sh | ||
docker-compose up --build | ||
``` | ||
|
||
This will start both DB and the telemetry server. The server will be available at `http://localhost:8080`. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think we could use a library like config to handle config, envs and cli args all at once declaratively? This should also remove the need of the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package cli | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/docopt/docopt-go" | ||
) | ||
|
||
// Environment variable support: | ||
// PYREVT_TELEMETRY_DB_CONNSTRING | ||
// PYREVT_TELEMETRY_SCRIPTS_TABLE | ||
// PYREVT_TELEMETRY_EVENTS_TABLE | ||
// PYREVT_TELEMETRY_PORT | ||
// PYREVT_TELEMETRY_HTTPS | ||
// PYREVT_TELEMETRY_DEBUG | ||
// PYREVT_TELEMETRY_TRACE | ||
// PYREVT_TELEMETRY_EXENAME | ||
// PYREVT_TELEMETRY_VERSION | ||
|
||
type Options struct { | ||
ExeName string `json:"exe_name"` | ||
Version string `json:"version"` | ||
Opts *docopt.Opts | ||
ConnString string `json:"connection_string"` | ||
ScriptsTable string `json:"script_table"` | ||
EventsTable string `json:"events_table"` | ||
Port int `json:"server_port"` | ||
Https bool `json:"https"` | ||
Debug bool `json:"debug_mode"` | ||
Trace bool `json:"trace_mode"` | ||
} | ||
|
||
func getExeName() string { | ||
return strings.TrimSuffix( | ||
filepath.Base(os.Args[0]), | ||
filepath.Ext(os.Args[0]), | ||
) | ||
} | ||
|
||
func NewOptions() *Options { | ||
argv := os.Args[1:] | ||
|
||
parser := &docopt.Parser{ | ||
HelpHandler: printHelpAndExit, | ||
} | ||
|
||
opts, _ := parser.ParseArgs(help, argv, version) | ||
|
||
connString, _ := opts.String("<db_uri>") | ||
scriptTable, _ := opts.String("--scripts") | ||
eventTable, _ := opts.String("--events") | ||
port, _ := opts.Int("--port") | ||
https, _ := opts.Bool("--https") | ||
|
||
debug, _ := opts.Bool("--debug") | ||
trace, _ := opts.Bool("--trace") | ||
|
||
// Environment variable fallback | ||
if connString == "" { | ||
connString = os.Getenv("PYREVT_TELEMETRY_DB_CONNSTRING") | ||
} | ||
if scriptTable == "" { | ||
scriptTable = os.Getenv("PYREVT_TELEMETRY_SCRIPTS_TABLE") | ||
} | ||
if eventTable == "" { | ||
eventTable = os.Getenv("PYREVT_TELEMETRY_EVENTS_TABLE") | ||
} | ||
Comment on lines
+65
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is another ranting for the future: do we really need to have dynamic table names? Especially now that you're introducing the APIs to get the data out of the DB.... |
||
if port == 0 { | ||
if portStr := os.Getenv("PYREVT_TELEMETRY_PORT"); portStr != "" { | ||
if p, err := strconv.Atoi(portStr); err == nil { | ||
port = p | ||
} | ||
} | ||
} | ||
if !https { | ||
if httpsStr := os.Getenv("PYREVT_TELEMETRY_HTTPS"); httpsStr != "" { | ||
if httpsStr == "1" || strings.ToLower(httpsStr) == "true" { | ||
https = true | ||
} | ||
} | ||
} | ||
if !debug { | ||
if debugStr := os.Getenv("PYREVT_TELEMETRY_DEBUG"); debugStr != "" { | ||
if debugStr == "1" || strings.ToLower(debugStr) == "true" { | ||
debug = true | ||
} | ||
} | ||
} | ||
if !trace { | ||
if traceStr := os.Getenv("PYREVT_TELEMETRY_TRACE"); traceStr != "" { | ||
if traceStr == "1" || strings.ToLower(traceStr) == "true" { | ||
trace = true | ||
} | ||
} | ||
} | ||
|
||
exeName := getExeName() | ||
if envExe := os.Getenv("PYREVT_TELEMETRY_EXENAME"); envExe != "" { | ||
exeName = envExe | ||
} | ||
ver := version | ||
if envVer := os.Getenv("PYREVT_TELEMETRY_VERSION"); envVer != "" { | ||
ver = envVer | ||
} | ||
|
||
return &Options{ | ||
ExeName: exeName, | ||
Version: ver, | ||
Opts: &opts, | ||
ConnString: connString, | ||
ScriptsTable: scriptTable, | ||
EventsTable: eventTable, | ||
Port: port, | ||
Https: https, | ||
Debug: debug, | ||
Trace: trace, | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps a note for later... could we use slog here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. We could do that in the next refactor pr. Slog will provide a more structured logging. I will note it in the list of next updates to make. I can do it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should do that in a separate pr after this is merged? @sanzoghenzo because this already has too many changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, let's save it for later. I'll create new issues from these comments (and move/split the original issue from pyrevit repo) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cli | ||
|
||
import ( | ||
"log" | ||
|
||
"pkg.re/essentialkaos/ek.v10/fmtc" | ||
) | ||
|
||
type Logger struct { | ||
PrintDebug bool | ||
PrintTrace bool | ||
} | ||
|
||
func NewLogger(options *Options) *Logger { | ||
return &Logger{ | ||
PrintDebug: options.Debug, | ||
PrintTrace: options.Trace, | ||
} | ||
} | ||
|
||
func (m *Logger) Fatal(args ...interface{}) { | ||
log.Fatal(args...) | ||
} | ||
|
||
func (m *Logger) Debug(args ...interface{}) { | ||
if m.PrintDebug { | ||
log.Print(args...) | ||
} | ||
} | ||
|
||
func (m *Logger) Trace(args ...interface{}) { | ||
if m.PrintTrace { | ||
log.Print(args...) | ||
} | ||
} | ||
|
||
func (m *Logger) Print(args ...interface{}) { | ||
fmtc.Println(args...) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
const version string = "0.19" | ||
const help string = `Record pyRevit usage logs to database | ||
|
||
Usage: | ||
pyrevit-telemetryserver [<db_uri>] [--scripts=<script_table>] [--events=<event_table>] --port=<port> [--https] [--debug] [--trace] | ||
|
||
Options: | ||
-h --help show this screen | ||
-V --version show version | ||
--scripts=<script_table> target table or collection for script logs | ||
--events=<event_table> target table or collection for app event logs | ||
--port=<port> server port number to listen on | ||
--https secure connection, expects ./pyrevit-telemetryserver.key and ./pyrevit-telemetryserver.crt | ||
--debug print debug info | ||
--trace print trace info e.g. full json logs and sql queries | ||
|
||
Supports: | ||
postgresql: using github.com/lib/pq | ||
mongodb: using gopkg.in/mgo.v2 | ||
mysql: using github.com/go-sql-driver/mysql | ||
sqlserver: using github.com/denisenkom/go-mssqldb | ||
sqlite3: using github.com/mattn/go-sqlite3 | ||
|
||
Examples: | ||
pyrevit-telemetryserver postgres://user:[email protected]/mydb --scripts="pyrevitlogs" --events="appevents" --port=8080 --debug | ||
pyrevit-telemetryserver mongodb://user:pass@localhost:27017/mydb --scripts="pyrevitlogs" --events="appevents" --port=8080 | ||
pyrevit-telemetryserver "mysql:user:pass@tcp(localhost:3306)/tests" --scripts="pyrevitlogs" --port=8080 | ||
pyrevit-telemetryserver sqlserver://user:[email protected]?database=mydb --scripts="pyrevitlogs" --port=8080 | ||
pyrevit-telemetryserver sqlite3:data.db --scripts="pyrevitlogs" --port=8080 | ||
` | ||
|
||
var printHelpAndExit = func(err error, docoptMessage string) { | ||
if err != nil { | ||
// if err occured print full help | ||
// docopt only includes usage section in its message | ||
fmt.Fprint(os.Stderr, help) | ||
os.Exit(1) | ||
} else { | ||
// otherwise print whatever docopt says | ||
// e.g. reporting version | ||
fmt.Println(docoptMessage) | ||
os.Exit(0) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
module pyrevittelemetryserver | ||
|
||
go 1.23.0 | ||
|
||
toolchain go1.24.2 | ||
|
||
require ( | ||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d | ||
github.com/denisenkom/go-mssqldb v0.11.0 | ||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 | ||
github.com/go-sql-driver/mysql v1.6.0 | ||
github.com/gofrs/uuid v4.3.1+incompatible | ||
github.com/gorilla/mux v1.8.0 | ||
github.com/lib/pq v1.10.3 | ||
github.com/mattn/go-sqlite3 v1.14.8 | ||
github.com/pkg/errors v0.9.1 | ||
github.com/satori/go.uuid v1.2.0 | ||
go.mongodb.org/mongo-driver v1.11.1 | ||
pkg.re/essentialkaos/ek.v10 v12.32.0+incompatible | ||
) | ||
|
||
require github.com/stretchr/testify v1.10.0 // indirect | ||
|
||
require ( | ||
// Indirect dependencies | ||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect | ||
github.com/golang/snappy v0.0.1 // indirect | ||
github.com/klauspost/compress v1.13.6 // indirect | ||
github.com/kr/pretty v0.2.1 // indirect | ||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect | ||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect | ||
github.com/xdg-go/scram v1.1.1 // indirect | ||
github.com/xdg-go/stringprep v1.0.3 // indirect | ||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect | ||
golang.org/x/crypto v0.35.0 // indirect | ||
golang.org/x/sync v0.11.0 // indirect | ||
golang.org/x/text v0.22.0 // indirect | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||
pkg.re/essentialkaos/check.v1 v1.0.0 // indirect | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go.mod uses: "go 1.23.0" and "toolchain go1.24.2", should this variable reflect that?