Handle Jaeger errors - go

I am planning to use Jaeger tracing in on my Golang server. Everything is ok but I haven't found a way to handle Jaeger errors. I want to catch, for example, connection error to Jaeger backend while sending trace and write it to loggly.
Code example:
package main
import (
"fmt"
"log"
"golang.org/x/net/context"
"contrib.go.opencensus.io/exporter/jaeger"
"go.opencensus.io/trace"
)
func main() {
agentEndpointURI := "localhost:6831"
collectorEndpointURI := "http://localhost:14268/api/traces"
je, err := jaeger.NewExporter(jaeger.Options{
AgentEndpoint: agentEndpointURI,
CollectorEndpoint: collectorEndpointURI,
ServiceName: "example",
})
if err != nil {
log.Fatalf("Failed to create the Jaeger exporter: %v", err)
}
trace.RegisterExporter(je)
trace.ApplyConfig(trace.Config{
DefaultSampler: trace.AlwaysSample(),
})
traceMethod()
}
func traceMethod() {
ctx := context.Background()
ctx, span := trace.StartSpan(ctx, "traceMethod")
// Do something here if the trace doesn't reach jaeger backend, write error to loggly
fmt.Println("send trace")
defer span.End()
}

Related

How to convert string into protoreflect.ProtoMessage or into anypb

I'm trying to use server sent event eventsoource through gRPC gateway, so read this
and this and came out with the below proto
syntax = "proto3";
package protobuf;
option go_package = "/proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";
service Events {
rpc StreamEvents (google.protobuf.Empty) returns (stream EventSource) {
option (google.api.http) = {
get: "/v1/rawdata/stream"
};
}
}
message Empty{}
message EventSource {
string event = 1;
google.protobuf.Any data = 2;
}
And generated all required gRPC files (Message/Service/Gateway) using:
protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
--grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
./proto/data.proto
And trying to registered the generated service and gateway at my main app, as:
package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
_ "github.com/mattn/go-sqlite3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "walistner/proto"
)
func main() {
gRPcPort := ":50005"
// Create a gRPC listener on TCP port
lis, err := net.Listen("tcp", gRPcPort)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// Create a gRPC server object
s := grpc.NewServer()
// Attach the Greeter service to the server
pb.RegisterEventsServer(s, server{})
log.Println("Serving gRPC server on 0.0.0.0:50005")
// Serve gRPC server
grpcTerminated := make(chan struct{})
go func() {
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
close(grpcTerminated) // In case server is terminated without us requesting this
}
}()
// Create a client connection to the gRPC server we just started
// This is where the gRPC-Gateway proxies the requests
// gateWayTarget := fmt.Sprintf("0.0.0.0%s", gRPcPort)
conn, err := grpc.DialContext(
context.Background(),
gRPcPort,
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalln("Failed to dial server:", err)
}
gwmux := runtime.NewServeMux()
// This handle to use JSON ServerSentEvent, not the gRPC one
//http.HandleFunc("/sse", passer.HandleSignal)
// Register custom route for GET /hello/{name}
//err = gwmux.HandlePath("GET", "/sse", passer.HandleSignal)
//if err != nil {
// fmt.Println("Error:", err)
//}
// This handler just to confirm server is up and running
// Register custom route for GET /hello/{name}
err = gwmux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Write([]byte("hello " + pathParams["name"]))
})
if err != nil {
fmt.Println("Error:", err)
}
// Register the gRPC ServerSentEvent
err = pb.RegisterEventsHandler(context.Background(), gwmux, conn)
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}
gwServer := &http.Server{
Addr: ":8090",
Handler: allowCORS(gwmux),
}
log.Println("Serving gRPC-Gateway on http://localhost:8090")
fmt.Println("run POST request of: http://localhost:8090/v1/rawdata/stream")
fmt.Println("or run curl -X GET -k http://localhost:8090/v1/rawdata/stream")
log.Fatal(gwServer.ListenAndServe()) // <- This line alone could be enough ang no need for all the lines after,
// the application is probably doing other things and you will want to be
// able to shutdown cleanly; passing in a context is a good method..
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Ensure cancel function is called eventually
grpcWebTerminated := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
if err := gwServer.ListenAndServe(); err != nil {
fmt.Printf("Web server (GRPC) shutdown: %s", err)
}
close(grpcWebTerminated) // In case server is terminated without us requesting this
}()
// Wait for the web server to shutdown OR the context to be cancelled...
select {
case <-ctx.Done():
// Shutdown the servers (there are shutdown commands to request this)
case <-grpcTerminated:
// You may want to exit if this happens (will be due to unexpected error)
case <-grpcWebTerminated:
// You may want to exit if this happens (will be due to unexpected error)
}
// Wait for the goRoutines to complete
<-grpcTerminated
<-grpcWebTerminated
// Listen to Ctrl+C (you can also do something else that prevents the program from exiting)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
if client.IsConnected() {
passer.data <- sseData{
event: "notification",
message: "Server is shut down at the host machine...",
}
client.Disconnect()
}
}
// allowCORS allows Cross Origin Resoruce Sharing from any origin.
// Don't do this without consideration in production systems.
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
h.ServeHTTP(w, r)
})
}
And the main part that is having an issue, is ther gateway registration:
package main
import (
"fmt"
"log"
"sync"
pb "walistner/proto"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/emptypb"
)
type server struct {
pb.UnimplementedEventsServer
}
func (s server) StreamEvents(_ *emptypb.Empty, srv pb.Events_StreamEventsServer) error {
if p, ok := peer.FromContext(srv.Context()); ok {
fmt.Println("Client ip is:", p.Addr.String())
}
md := metadata.New(map[string]string{"Content-Type": "text/event-stream", "Connection": "keep-alive"})
srv.SetHeader(md)
//use wait group to allow process to be concurrent
var wg sync.WaitGroup
for {
incomingInput := <-passer.data
wg.Add(1)
//go func(count int64) {
go func() {
defer wg.Done()
myData := incomingInput.data <---------------This is a string
// Convert data to *anypb.Any
data, err := anypb.New(myData.(protoreflect.ProtoMessage)) <----- tried this but it failed
if err != nil {
//...
}
resp := pb.EventSource{
Event: incomingInput.event,
Data: data, // <---------- this is required to be of type *anypb.Any
}
if err := srv.Send(&resp); err != nil {
log.Printf("send error %v", err)
}
}()
}
wg.Wait()
return nil
}
My data structure inside the go app is:
type sseData struct {
event, message string
}
type DataPasser struct {
data chan sseData
logs chan string
connection chan struct{} // To control maximum allowed clients connections
}
var passer *DataPasser
func init() {
passer = &DataPasser{
data: make(chan sseData),
logs: make(chan string),
connection: make(chan struct{}, maxClients),
}
}

go build doesn't recognise methods

I try to setup a small Golang Microservice for users with Gin and Mongodb.
package main
import (
"context"
"fmt"
"github.com/wzslr321/artiver/entity"
"github.com/wzslr321/artiver/settings"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
"os"
"os/signal"
"syscall"
"time"
)
type application struct {
users *entity.UserCollection
}
var app *application
func init() {
initMongo()
}
func initMongo() {
oc := options.Client().ApplyURI(settings.MongodbSettings.Uri)
client, err := mongo.NewClient(oc)
if err != nil {
log.Fatalf("Error occured while initializing a new mongo client: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
log.Fatalf("Errorr occurred while connecting to a client: %v", err)
}
defer func() {
if err = client.Disconnect(ctx); err != nil {
panic(err)
}
}()
log.Println("Successfully connected to the database!")
app = &application{
users: &entity.UserCollection{
C: client.Database("artiver").Collection("users"),
},
}
}
func main() {
router := app.InitRouter()
It doesn't show any errors in my IDE ( GoLand ), but when I try to build it I get an error:
# command-line-arguments
users/cmd/app/main.go:67:15: app.InitRouter undefined (type *application has no field or method InitRouter)
It it easily visible on the image above, that I do have access to such a method. It is defined in the same package.
package main
import (
"github.com/gin-gonic/gin"
cors "github.com/rs/cors/wrapper/gin"
"net/http"
)
func (app *application) InitRouter() *gin.Engine {
r := gin.New()
r.Use(gin.Recovery())
r.Use(cors.Default())
r.GET("/", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "Hello World")
})
user := r.Group("/api/user")
{
user.POST("/add", app.CreateUser)
}
return r
}
I have no idea how am I supposed to fix it and what is done wrong. I'd appreciate any hint about what isn't done correctly.
Answer based on #mkopriva help in comments.
The issue was related to not running all needed .go files.
In my case, the solution was to build it this way in my Makefile:
go build -o $(path)users cmd/app/*
In similar cases, go run . most likely will do the job.

Connection error rpc with Golang and DGraph

i'm trying make a mutation inside a DGraph database, but when i run the code, it throws me the next error:
rpc error: code = Unavailable desc = connection close exit status 1
I'm using dGraph with docker in the port 8000, my code of golang here:
package main
import (
"fmt"
"context"
"encoding/json"
"log"
dgo "github.com/dgraph-io/dgo"
api "github.com/dgraph-io/dgo/protos/api"
grpc "google.golang.org/grpc"
)
type Person struct {
Name string `json:"name,omitempty"`
Lastname string `json:"lastname,omitempty"`
}
func main() {
conn, err := grpc.Dial("localhost:8000", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
dgraphClient := dgo.NewDgraphClient(api.NewDgraphClient(conn))
p := Person {
Name: "Giovanni",
Lastname: "Mosquera Diazgranados",
}
txn := dgraphClient.NewTxn()
ctx := context.Background()
defer txn.Discard(ctx)
pb, err := json.Marshal(p)
if err != nil {
log.Fatal(err)
}
mu := &api.Mutation{
SetJson: pb,
}
res, err := txn.Mutate(ctx, mu)
if err != nil {
fmt.Println("Aqui toy")
log.Fatal(err)
} else {
fmt.Println(res)
}
}
How can i solve this error to connect with my DGraph and make a mutation?
Welcome to Stack Overflow!
To get your code working locally with the docker "standalone" version of DGraph I had to change 2 things:
use port 9080. The container exposes 3 ports: 8000, 8080, 9080. Using 8080 or 8000 I get the same error you mentioned.
use the v2 imports. Not sure which version of DGraph server you are running, so you might not need to do this. But in case you have a new server you need these imports:
import (
dgo "github.com/dgraph-io/dgo/v2"
api "github.com/dgraph-io/dgo/v2/protos/api"
)
Port 8000 is for the ratel-ui which comes with dgraph. To make mutations using the dgraph go client you will want to connect to the exposed grpc-alpha port, this is typically on 9080.

How should be a Go GRPC server for this example client?

We are trying to simulate a GRPC service for testing purposes but we are getting problems with creating the server. Our client it's working with our GRPC provider and it is similar to the next code.
We are having the problem with the configuration of the server that receive requests with a nil
as credentials.
package main
import (
"context"
"flag"
"fmt"
"log"
"time"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc"
ecpb "google.golang.org/grpc/examples/features/proto/echo"
)
var addr = flag.String("addr", "localhost:50051", "the address to connect to")
func callUnaryEcho(client ecpb.EchoClient, message string) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := client.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
if err != nil {
log.Fatalf("client.UnaryEcho(_) = _, %v: ", err)
}
fmt.Println("UnaryEcho: ", resp.Message)
}
func main() {
flag.Parse()
ctx := context.TODO()
conn, err := grpc.DialContext(
ctx,
*addr,
grpc.WithTransportCredentials(credentials.NewTLS(nil)),
)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
rgc := ecpb.NewEchoClient(conn)
callUnaryEcho(rgc, "hello world")
}

http server Websocket and serving a static page

Consider the following sample
import (
"code.google.com/p/go.net/websocket"
"fmt"
"log"
"net/http"
"os"
)
type MessageReceived struct {
Name string
Nbmsg int
}
func Echo(ws *websocket.Conn) {
msg := new(MessageReceived)
for {
websocket.JSON.Receive(ws, &msg)
fmt.Printf("Received message: %+v\n", msg)
if err := websocket.JSON.Send(ws, msg); err != nil {
fmt.Println("Can't send echo")
break
}
//os.Exit(0)
}
}
func checkError(err error) {
if err != nil {
log.Printf("Fatal error: %s\n", err.Error())
os.Exit(1)
}
}
func main() {
http.Handle("/s", websocket.Handler(Echo))
fs := http.FileServer(http.Dir("web"))
err := http.ListenAndServe(":8081",fs)
checkError(err)
}
The page and static files gets served but when but i try to connect to the websocket from javascript i get
WebSocket connection to 'ws://localhost:8081/s' failed: Error during WebSocket handshake: Unexpected response code: 404
The only way i could make it work is by serving static files from a different go application server and serving the websocket also from a separate server application using 2 different ports on the local host, what am i missing in the above sample? how could i serve files and the websocket from the same server?
You're assigning the websocket.Handler to the DefaultServeMux, but only using the FileServer handler in your ListenAndServe call.
Add the FileServer handler to the DefaultServe mux too, with some non-conflicting prefix:
func main() {
http.Handle("/s", websocket.Handler(Echo))
http.Handle("/f", http.FileServer(http.Dir("web"))
err := http.ListenAndServe(":8081", nil)
checkError(err)
}

Resources