Sentry Go Integration, how to specify error level? - go

According to the official docs https://docs.sentry.io/clients/go/ you can log errors in Sentry from a golang project with:
// For Errors
raven.CapturePanic(func() {
// do all of the scary things here
}, nil)
// For panic
if err != nil {
raven.CaptureErrorAndWait(err, nil)
log.Panic(err)
}
This works like a charm, the problem is in Sentry both functions are logged with level 'Error'. Anyone knows how can the logging level be specified for each call? In Python is very explicit, but I don't see it for Go.

Using the sentry-go SDK, the Level is set on the Scope.
Documentation:
https://pkg.go.dev/github.com/getsentry/sentry-go?tab=doc#Scope.SetLevel
https://pkg.go.dev/github.com/getsentry/sentry-go?tab=doc#Level
Example:
sentry.WithScope(func(scope *sentry.Scope) {
scope.SetLevel(sentry.LevelFatal)
sentry.CaptureException(errors.New("example error"))
})

I followed the advice in the comments, and came up with this:
// sentryErrorCapture sends error data to Sentry asynchronously. Use for non-Fatal errors.
var sentryErrorCapture = func(err error, severity raven.Severity, tags map[string]string, interfaces ...raven.Interface) string {
packet := newSentryPackage(err, severity, tags, interfaces...)
eventID, _ := raven.Capture(packet, tags)
return eventID
}
func newSentryPackage(err error, severity raven.Severity, tags map[string]string, interfaces ...raven.Interface) (packet *raven.Packet) {
interfaces = append(interfaces,
raven.NewException(err, raven.GetOrNewStacktrace(err, 1, 3, raven.IncludePaths())))
packet = &raven.Packet{
Message: err.Error(),
Level: severity,
Interfaces: interfaces,
Extra: getSentryExtraInfo(),
}
return
}
When I want to log an error specifying the level I call: sentryErrorCapture(err, raven.ERROR, nil).

Related

DynamoDB - Error is ResourceNotFoundException but cast fails in Go

I wrote some code to attempt to get a table description in Go, using the AWS SDK V2 DynamoDB package:
// First, create a connection to our local DynamoDB
client := dynamodb.NewFromConfig(cfg)
// Next, attempt to get the table description associated with the table name
output, err := client.DescribeTable(ctx, &dynamodb.DescribeTableInput{
TableName: table.TableName,
})
// Now, if we got an error then check if it was a resource-not-found exception. If
// it was then that means we should create the table; otherwise, it means that something
// isn't right so return it. If the description was nil, we'll also create the table
var create bool
if err != nil {
if _, ok := err.(*types.ResourceNotFoundException); !ok {
return err
} else {
create = true
}
} else if output == nil {
create = true
}
During testing, this code returned the following error:
operation error DynamoDB: DescribeTable, https response error StatusCode: 400, RequestID: 4b0bcb2b-c833-459f-9db2-54841aa1bbd3, ResourceNotFoundException
The problem I'm having is that this is clearly a ResourceNotFoundException but the cast is not working. Is there something else I need to do to get this to work?
I have found a solution. First, one of the issues I had was that I was importing github.com/aws/aws-sdk-go-v2/service/sso/types instead of github.com/aws/aws-sdk-go-v2/service/dynamodb/types so my cast would never have worked. That being said, making this change did not fix the issue.
After log-debugging, I discovered that the v2 SDK wraps the AWS errors in a *smithy.OperationError type. Therefore, direct-casting and errors.Is won't work.
What I did that actually worked here was to change my error checking code to this:
if temp := new(types.ResourceNotFoundException); !errors.As(err, &temp) {
return err
} else {
create = true
}

GO Gin Keeps returning empty status if Any part of my code returns false

I am relatively new to Golang and Gin(framework)
I am writing some really simple API endpoints . But I notice something really weird about Gin, if there is any codition or function within my code that returns false the rest of my code or conditions does not get executed and Gin just returns a JSON with empty status :
{"status":""}
Here is a very simple code to explain what I mean
In a functions.go file I have :
func VerifyUserLogin(username,password,userAgent string) (string, string) {
userData := Users{}
userQueryColumn := "username=?"
// if they are trying to login with email
if nEmailHelpers.EmailIsValid(username) == true{
userQueryColumn = "email=?"
}
if getUserData := db.Select("password").Where(userQueryColumn, strings.ToLower(username)).First(&userData); getUserData.Error != nil {
// Meaning there was an error, most likely no data found , so we just return false todo improve this error handling later to handle more specific errors
return "", feedback["InvalidLogin"]
} else {
if getUserData.RowsAffected == 1 {
if nSecurityHelpers.CheckPasswordHash(password, userData.Password)==true {
token, tokenError := CreateToken(username, userAgent, false, 60)
if tokenError == nil {
return token, feedback["ValidLogin"]
} else {
return "", feedback["TokenNotGenerated"]
}
} else {
return "", feedback["InvalidLogin"]
}
}
return "", feedback["InvalidLogin"]
}
}
In another go file that references the functions.go file I have :
func main(){
router := gin.Default()
router.POST ("login",loginUser)
router.Run()
}
var feedback = userFeedback.Users()
// loginUser function to login a user
func loginUser(c *gin.Context){
requestBody := neielRequestsHelpers.RequestBody(c)
username := requestBody["username"]
password := requestBody["password"]
userAgent := c.Request.Header.Get("User-Agent")
token, loginMessage := userFunctions.VerifyUserLogin(username,password,userAgent)
// todo come back and fix proper error message when user does not exist
fmt.Println(loginMessage)
if loginMessage==feedback["ValidLogin"]{
c.JSON(http.StatusOK, gin.H{"status":loginMessage,"token":token})
}else{
c.JSON(http.StatusUnauthorized, gin.H{"status":feedback["InvalidLogin"]})
}
}
If all my inputs are correct , all goes well (Username exists and password is correct). But I have to handle scenario where username or password is invalid .If for any reason getUserData or nSecurityHelpers.CheckPasswordHash() is false , or any function for that matter returns a boolean of false . The entire function just terminates and doesn't allow me handle the error the way I want and output custom JSON response. I just get this {"status":""}
I am 100% sure this issue is from Gin , but I don't know what to activate or deactivate to allow me handle errors on my own. I have read the docs, but its obvious I am missing something .
Kindly help me please .
I have resolved this, thanks to everyone that tried to help
It was a typo error causing the issue "InValidLogin" instead of "InvalidLogin" in another file.
It was really subtle

Google Directory API add Custom Schema/Update it to Users per google API (in go)

I am trying to upload a CustomSchema to all Users of a company in GSuite. This Custom Schema contains their Github Usernames, which I extracted with the github API.
The problem is, after running the code, the account in Gsuite is not added.
Relevant code (A connection to GSuite with admin Authentication is established, the map has all user entries. If you still want more code, I can provide you with it - just trying to keep it simple):
for _, u := range allUsers.Users {
if u.CustomSchemas != nil {
log.Printf("%v", string(u.CustomSchemas["User_Names"]))
}else{
u.CustomSchemas = map[string]googleapi.RawMessage{}
}
nameFromGsuite := u.Name.FullName
if githubLogin, ok := gitHubAccs[nameFromGsuite]; ok {
userSchemaForGithub := GithubAcc{GitHub: githubLogin}
jsonRaw, err := json.Marshal(userSchemaForGithub)
if err != nil {
log.Fatalf("Something went wrong logging: %v", err)
}
u.CustomSchemas["User_Names"] = jsonRaw
adminService.Users.Update(u.Id, u)
} else {
log.Printf("User not found for %v\n", nameFromGsuite)
}
}
This is the struct for the json encoding:
type GithubAcc struct {
GitHub string `json:"GitHub"`
}
For anyone stumbling upon this.
Everything in the code snippet is correct. By the way the method is written, I expected that adminService.Users.Update() actually updates the user. Instead, it returns an UserUpdatesCall.
You need to execute that update by calling .Do()
From the API:
Do executes the "directory.users.update" call.
So the solution is to change adminService.Users.Update(u.Id, u)
into adminService.Users.Update(u.Id, u).Do()

Catch error code from GCP pub/sub

I am using go package for pub/sub. On my API dashboard I see this error(google.pubsub.v1.Subscriber.StreamingPull - error code 503). Per docs(https://cloud.google.com/pubsub/docs/reference/error-codes) it seems it is transient condition but better to implement backoff strategy(https://cloud.google.com/storage/docs/exponential-backoff). the question is I am not able to wrap my head where this error code is coming on Receive method.
Here is func:
err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) {
// Dump message
// log.Printf("Got message: %s", m.Data)
// Decoding coming message
err = json.NewDecoder(bytes.NewReader(m.Data)).Decode(&msg)
if err != nil {
log.Printf("Error decoding - %v", err)
}
// See streaming messages
log.Printf(" %s : %s : Product updated for Product Id(%d) : Product Title(%s)",
msg.AuthID,
msg.TraceID,
msg.Product.ID,
msg.Product.Title,
)
//
// Some business logic
//
// Acknowledge on recieve method
m.Ack()
})
if err != context.Canceled {
// if err != nil {
return errors.Wrap(err, "Error occurred on recieve data from topic: blah")
}
The Cloud Pub/Sub Go client library will retry this transient error on its own, you shouldn't need to handle it. Internally, the client library uses StreamingPull, where it sends a request and receives messages from the service as they are available. Occasionally, there can be a disconnection event requiring the connection to be reestablished. This is why you see the 503 error in the API dashboard. It should not be the case that your code sees an error in this scenario since the underlying library is handling it (including the use of exponential backoff, where relevant).

Selectively Follow Redirects in Go

I'm trying to write a twitter reader that resolves the final URLs of link shorteners etc, but gives me a URL along the way for a list of manually defined host patterns. The reason to do this is that i don't want to end up with the paywall URL but the one before.
As far as i can tell the way to do this is write my own client based on the default RoundTripper because returning an error from a custom CheckRedirect function aborts the client without yielding a response.
Is there a way to use the default client and record a list of URLs/specific URL from a custom checkRedirect function?
The client request will actually still return the last valid Response in cases where your custom CheckResponse yields an error (As mentioned in the comments).
http://golang.org/pkg/net/http/#Client
If CheckRedirect returns an error, the Client's Get method returns both the previous Response and CheckRedirect's error (wrapped in a url.Error) instead of issuing the Request req.
If you maintain a list of "known" paywall-urls, you can abort the paywall-redirect in your CheckResponse with a custom error type (Paywalled in the example below).
Your error handling code later has to consider that error type as a special (non-erroneous) case.
Example:
package main
import (
"errors"
"fmt"
"net/http"
"net/url"
)
var Paywalled = errors.New("next redirect would hit a paywall")
var badHosts = map[string]error{
"registration.ft.com": Paywalled,
}
var client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// N.B.: when used in production, also check for redirect loops
return badHosts[req.URL.Host]
},
}
func main() {
resp, err := client.Get("http://on.ft.com/14pQBYE")
// ignore non-nil err if it's a `Paywalled` wrapped in url.Error
if e, ok := err.(*url.Error); (ok && e.Err != Paywalled) || (!ok && err != nil) {
fmt.Println("error: ", err)
return
}
resp.Body.Close()
fmt.Println(resp.Request.URL)
}

Resources