Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions example/sqlx-oracle/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM oraclelinux:8-slim

RUN microdnf install -y wget unzip libaio golang git && \
microdnf clean all

WORKDIR /tmp
RUN wget https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip && \
unzip instantclient-basic-linuxx64.zip && \
mkdir -p /opt && \
mv instantclient_* /opt/instantclient && \
rm -f instantclient-basic-linuxx64.zip

WORKDIR /app

COPY . .

ENV LD_LIBRARY_PATH=/opt/instantclient
RUN CGO_ENABLED=1 go build -o app main.go

CMD ["./app"]
98 changes: 98 additions & 0 deletions example/sqlx-oracle/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
services:
oracle-primary:
image: gvenzl/oracle-free:latest
container_name: hostdb1
hostname: hostdb1
ports:
- "1521:1521"
environment:
- ORACLE_PASSWORD=OraclePass123
- APP_USER=scott
- APP_USER_PASSWORD=tiger
volumes:
- oracle_primary_data:/opt/oracle/oradata
healthcheck:
test: ["CMD", "healthcheck.sh"]
interval: 30s
timeout: 10s
retries: 5
networks:
app_network:
aliases:
- hostdb1

oracle-standby:
image: gvenzl/oracle-free:latest
container_name: hostdb2
hostname: hostdb2
ports:
- "1522:1521"
environment:
- ORACLE_PASSWORD=OraclePass123
- APP_USER=scott
- APP_USER_PASSWORD=tiger
volumes:
- oracle_standby_data:/opt/oracle/oradata
healthcheck:
test: ["CMD", "healthcheck.sh"]
interval: 30s
timeout: 10s
retries: 5
networks:
app_network:
aliases:
- hostdb2

instana-agent:
image: icr.io/instana/agent:latest
container_name: instana_agent
pid: "host"
privileged: true
ports:
- "42699:42699"
environment:
- INSTANA_AGENT_KEY=${INSTANA_AGENT_KEY}
- INSTANA_DOWNLOAD_KEY=${INSTANA_DOWNLOAD_KEY}
- INSTANA_AGENT_ENDPOINT=${INSTANA_AGENT_ENDPOINT:-ingress-red-saas.instana.io}
- INSTANA_AGENT_ENDPOINT_PORT=443
- INSTANA_ZONE=oracle-test
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /dev:/dev
- /sys:/sys
- /var/log:/var/log
networks:
- app_network

go-app:
build:
context: .
dockerfile: Dockerfile
container_name: go_oracle_app
ports:
- "8080:8080"
environment:
- ORACLE_CONNECTION_STRING=scott/tiger@(description=(CONNECT_TIMEOUT=40)(RETRY_COUNT=10)(TRANSPORT_CONNECT_TIMEOUT=3)(address_list=(address=(protocol=tcp)(host=hostdb1)(port=1521))(address=(protocol=tcp)(host=hostdb2)(port=1521))(failover=on)(load_balance=off))(connect_data=(service_name=FREEPDB1)))
- INSTANA_AGENT_HOST=instana-agent
- INSTANA_AGENT_PORT=42699
- INSTANA_LOG_LEVEL=info
- LD_LIBRARY_PATH=/opt/instantclient
- DYLD_LIBRARY_PATH=/opt/instantclient
- PATH=/opt/instantclient:${PATH}
depends_on:
oracle-primary:
condition: service_healthy
oracle-standby:
condition: service_healthy
instana-agent:
condition: service_started
networks:
- app_network

volumes:
oracle_primary_data:
oracle_standby_data:

networks:
app_network:
driver: bridge
21 changes: 21 additions & 0 deletions example/sqlx-oracle/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module sqlx-go-oracle.com

go 1.23.0
require (
github.com/godror/godror v0.49.5
github.com/instana/go-sensor v1.71.2
github.com/jmoiron/sqlx v1.4.0
)

require (
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/godror/knownpb v0.3.0 // indirect
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/looplab/fsm v1.0.3 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
59 changes: 59 additions & 0 deletions example/sqlx-oracle/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/UNO-SOFT/zlog v0.8.1 h1:TEFkGJHtUfTRgMkLZiAjLSHALjwSBdw6/zByMC5GJt4=
github.com/UNO-SOFT/zlog v0.8.1/go.mod h1:yqFOjn3OhvJ4j7ArJqQNA+9V+u6t9zSAyIZdWdMweWc=
github.com/VictoriaMetrics/easyproto v0.1.4 h1:r8cNvo8o6sR4QShBXQd1bKw/VVLSQma/V2KhTBPf+Sc=
github.com/VictoriaMetrics/easyproto v0.1.4/go.mod h1:QlGlzaJnDfFd8Lk6Ci/fuLxfTo3/GThPs2KH23mv710=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/godror/godror v0.49.5 h1:8w2LaxfH1bgrZwtT4OX3G//ld9AUDsZMxZu89NaCTLk=
github.com/godror/godror v0.49.5/go.mod h1:kTMcxZzRw73RT5kn9v3JkBK4kHI6dqowHotqV72ebU8=
github.com/godror/knownpb v0.3.0 h1:+caUdy8hTtl7X05aPl3tdL540TvCcaQA6woZQroLZMw=
github.com/godror/knownpb v0.3.0/go.mod h1:PpTyfJwiOEAzQl7NtVCM8kdPCnp3uhxsZYIzZ5PV4zU=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/instana/go-sensor v1.71.2 h1:xjAJiE2RrhRi0vMoK7I8jnL9TYabgyfKgTHUkqCsWwk=
github.com/instana/go-sensor v1.71.2/go.mod h1:wWLB5TQn5zd+XxZPLkaScMzRr74ymtptaDTPhrueDyM=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/looplab/fsm v1.0.3 h1:qtxBsa2onOs0qFOtkqwf5zE0uP0+Te+wlIvXctPKpcw=
github.com/looplab/fsm v1.0.3/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=
github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
135 changes: 135 additions & 0 deletions example/sqlx-oracle/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package main

import (
"context"
"fmt"
"net/http"
"os"
"time"

"github.com/godror/godror"
instana "github.com/instana/go-sensor"
"github.com/jmoiron/sqlx"
)

var s instana.TracerLogger

func init() {
s = instana.InitCollector(&instana.Options{
Service: "Oracle app",
Tracer: instana.DefaultTracerOptions(),
LogLevel: instana.Info,
})
}

func getTNSConnString() string {
connStr := os.Getenv("ORACLE_CONNECTION_STRING")
if connStr == "" {
user := getEnvOrDefault("ORACLE_USER", "scott")
password := getEnvOrDefault("ORACLE_PASSWORD", "tiger")

tnsDescriptor := `(description=(CONNECT_TIMEOUT=40)(RETRY_COUNT=10)(TRANSPORT_CONNECT_TIMEOUT=3)` +
`(address_list=(address=(protocol=tcp)(host=hostdb1)(port=1521))` +
`(address=(protocol=tcp)(host=hostdb2)(port=1521))` +
`(failover=on)(load_balance=off))` +
`(connect_data=(service_name=ods-domain)))`

connStr = fmt.Sprintf("%s/%s@%s", user, password, tnsDescriptor)
}

return connStr
}

func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}

func initDbConn(connStr string) (*sqlx.DB, error) {
driverName := "oracle"

P, err := godror.ParseDSN(connStr)
if err != nil {
return nil, fmt.Errorf("failed to parse DSN: %w", err)
}

gDrv := godror.NewConnector(P).Driver()
instana.InstrumentSQLDriver(s.LegacySensor(), driverName, gDrv)

dbx, err := sqlx.Open(driverName+"_with_instana", connStr)
if err != nil {
return nil, fmt.Errorf("failed to open connection: %w", err)
}

dbx.SetMaxOpenConns(10)
dbx.SetMaxIdleConns(5)
dbx.SetConnMaxLifetime(time.Hour)
dbx.SetConnMaxIdleTime(10 * time.Minute)

ctx, cancel := context.WithTimeout(context.TODO(), 15*time.Second)
defer cancel()

if err = dbx.PingContext(ctx); err != nil {
return nil, fmt.Errorf("failed to ping database: %w", err)
}

fmt.Println("Successfully connected to Oracle database")
return dbx, nil
}

var dbConn *sqlx.DB

func handler(w http.ResponseWriter, req *http.Request) {
var result string
err := dbConn.QueryRowContext(req.Context(), `SELECT 'Connected to Oracle!' FROM DUAL`).Scan(&result)
if err != nil {
http.Error(w, fmt.Sprintf("Query error: %v", err), http.StatusInternalServerError)
return
}

_, _ = fmt.Fprintf(w, "Result: %s", result)
}

func healthHandler(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), 2*time.Second)
defer cancel()

if err := dbConn.PingContext(ctx); err != nil {
http.Error(w, "Database unhealthy", http.StatusServiceUnavailable)
return
}

w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "OK")
}

func main() {
connStr := getTNSConnString()
fmt.Println("Connecting to Oracle...")

var err error
dbConn, err = initDbConn(connStr)
if err != nil {
panic(fmt.Sprintf("Failed to initialize database connection: %v", err))
}
defer dbConn.Close()

fmt.Println("Waiting for Instana agent...")
for i := 0; i < 30; i++ {
if instana.Ready() {
fmt.Println("Instana agent ready")
break
}
time.Sleep(1 * time.Second)
}

http.HandleFunc("/oracle", instana.TracingHandlerFunc(s, "/oracle", handler))
http.HandleFunc("/health", healthHandler)

fmt.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
39 changes: 39 additions & 0 deletions instrumentation_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ func ParseDBConnDetails(connStr string) DbConnDetails {
parseMySQLConnDetailsKV,
parseRedisConnString,
parseDBConnDetailsURI,
parseOracleTNSSQLDriver,
}
for _, parseFn := range strategies {
if details, ok := parseFn(connStr); ok {
Expand Down Expand Up @@ -662,6 +663,44 @@ func parseRedisConnString(connStr string) (DbConnDetails, bool) {
return d, false
}

var (
myOracleTNSGoDriverRe = regexp.MustCompile(`(?i)^([^/@]+)(?:/[^@]*)?@.*?host=([^)(]+).*?port=(\d+)`)
oracleTnsSqlKVPasswordRegex = regexp.MustCompile(`(?i)^([^/@]+)/[^@]*@`)
)

func parseOracleTNSSQLDriver(connStr string) (DbConnDetails, bool) {

matches := myOracleTNSGoDriverRe.FindAllStringSubmatch(connStr, -1)

if len(matches) == 0 {
return DbConnDetails{}, false
}

values := matches[0]

host := values[2]
port := values[3]

if host == "" {
host = "localhost"
}

if port == "" {
port = "1521"
}

d := DbConnDetails{
User: values[1],
Host: host,
Port: port,
DatabaseName: "oracle",
}

d.RawString = oracleTnsSqlKVPasswordRegex.ReplaceAllString(connStr, `${1}/***@`)

return d, true
}

type dsnConnector struct {
dsn string
driver driver.Driver
Expand Down