@@ -8,30 +8,47 @@ import (
88 "io/ioutil"
99 "os"
1010 "path"
11+ "strconv"
1112 "strings"
1213
1314 "github.com/streadway/amqp"
1415)
1516
1617var (
17- uri = flag .String ("uri" , "amqp://guest:guest@localhost:5672/" , "AMQP URI" )
18- insecure_tls = flag .Bool ("insecure-tls" , false , "Insecure TLS mode: don't check certificates" )
19- queue = flag .String ("queue" , "" , "AMQP queue name" )
20- ack = flag .Bool ("ack" , false , "Acknowledge messages" )
21- maxMessages = flag .Uint ("max-messages" , 1000 , "Maximum number of messages to dump" )
22- outputDir = flag .String ("output-dir" , "." , "Directory in which to save the dumped messages" )
23- full = flag .Bool ("full" , false , "Dump the message, its properties and headers" )
24- verbose = flag .Bool ("verbose" , false , "Print progress" )
18+ uri = flag .String ("uri" , "amqp://guest:guest@localhost:5672/" , "AMQP URI" )
19+ insecure_tls = flag .Bool ("insecure-tls" , false , "Insecure TLS mode: don't check certificates" )
20+ queue = flag .String ("queue" , "" , "AMQP queue name" )
21+ ack = flag .Bool ("ack" , false , "Acknowledge messages" )
22+ maxMessages = flag .Uint ("max-messages" , 1000 , "Maximum number of messages to dump" )
23+ outputDir = flag .String ("output-dir" , "." , "Directory in which to save the dumped messages" )
24+ jsonContent = flag .Bool ("json-content" , true , "If the content of the message is a JSON object" )
25+ verbose = flag .Bool ("verbose" , false , "Print progress" )
26+ messagesToAckFile = flag .String ("messages-to-ack" , "" , "File with messages to ack" )
2527)
2628
29+ type Messages struct {
30+ Messages []Message `json:"messages"`
31+ }
32+
33+ type Message struct {
34+ RoutingKey string `json:"routingKey"`
35+ Contains []Contain `json:"contains"`
36+ ContainsString string `json:"containsString"`
37+ }
38+
39+ type Contain struct {
40+ Key string `json:"key"`
41+ Value interface {} `json:"value"`
42+ }
43+
2744func main () {
2845 flag .Parse ()
2946 if flag .NArg () > 0 {
3047 fmt .Fprintf (os .Stderr , "Error: Unused command line arguments detected.\n " )
3148 flag .Usage ()
3249 os .Exit (2 )
3350 }
34- err := DumpMessagesFromQueue (* uri , * queue , * maxMessages , * outputDir )
51+ err := DumpMessagesFromQueue (* uri , * queue , * maxMessages , * outputDir , * messagesToAckFile , * jsonContent )
3552 if err != nil {
3653 fmt .Fprintf (os .Stderr , "%s\n " , err )
3754 os .Exit (1 )
@@ -50,7 +67,28 @@ func dial(amqpURI string) (*amqp.Connection, error) {
5067 return conn , err
5168}
5269
53- func DumpMessagesFromQueue (amqpURI string , queueName string , maxMessages uint , outputDir string ) error {
70+ func DumpMessagesFromQueue (amqpURI string , queueName string , maxMessages uint , outputDir string , messagesToAckFilePath string , isContentJSON bool ) error {
71+ var messages Messages
72+ var ackMessage bool
73+ searchToAck := false
74+
75+ if messagesToAckFilePath != "" {
76+ jsonFile , err := os .Open (messagesToAckFilePath )
77+ if err != nil {
78+ return fmt .Errorf ("Opening messages to ack file: %s" , err )
79+ } else {
80+ byteValue , err := ioutil .ReadAll (jsonFile )
81+ if err != nil {
82+ return fmt .Errorf ("Reding sessages to ack file: %s" , err )
83+ }
84+
85+ json .Unmarshal (byteValue , & messages )
86+ jsonFile .Close ()
87+ byteValue = nil
88+ searchToAck = true
89+ }
90+ }
91+
5492 if queueName == "" {
5593 return fmt .Errorf ("Must supply queue name" )
5694 }
@@ -72,8 +110,9 @@ func DumpMessagesFromQueue(amqpURI string, queueName string, maxMessages uint, o
72110
73111 VerboseLog (fmt .Sprintf ("Pulling messages from queue %q" , queueName ))
74112 for messagesReceived := uint (0 ); messagesReceived < maxMessages ; messagesReceived ++ {
113+ ackMessage = * ack
75114 msg , ok , err := channel .Get (queueName ,
76- * ack , // autoAck
115+ ackMessage , // autoAck
77116 )
78117 if err != nil {
79118 return fmt .Errorf ("Queue get: %s" , err )
@@ -84,31 +123,54 @@ func DumpMessagesFromQueue(amqpURI string, queueName string, maxMessages uint, o
84123 break
85124 }
86125
87- err = SaveMessageToFile (msg .Body , outputDir , messagesReceived )
88- if err != nil {
89- return fmt .Errorf ("Save message: %s" , err )
90- }
91-
92- if * full {
93- err = SavePropsAndHeadersToFile (msg , outputDir , messagesReceived )
94- if err != nil {
95- return fmt .Errorf ("Save props and headers: %s" , err )
126+ if searchToAck {
127+ message_content := string (msg .Body [:])
128+ for i := len (messages .Messages ) - 1 ; i >= 0 ; i -- {
129+ if msg .RoutingKey == messages .Messages [i ].RoutingKey {
130+ containsNeeded := len (messages .Messages [i ].Contains )
131+ containsFound := 0
132+ for j := 0 ; j < containsNeeded ; j ++ {
133+ search := ""
134+ switch messages .Messages [i ].Contains [j ].Value .(type ) {
135+ case string :
136+ search = "\" " + messages .Messages [i ].Contains [j ].Key + "\" :\" " + messages .Messages [i ].Contains [j ].Value .(string ) + "\" "
137+ case float64 :
138+ val := messages .Messages [i ].Contains [j ].Value .(float64 )
139+ fmtp := "%." + strconv .Itoa (NumDecPlaces (val )) + "f"
140+ search = "\" " + messages .Messages [i ].Contains [j ].Key + "\" :" + fmt .Sprintf (fmtp , val )
141+ case bool :
142+ search = "\" " + messages .Messages [i ].Contains [j ].Key + "\" :" + fmt .Sprintf ("%t" , messages .Messages [i ].Contains [j ].Value .(bool ))
143+ }
144+
145+ if search != "" && strings .Contains (message_content , search ) {
146+ containsFound ++
147+ }
148+ }
149+
150+ if messages .Messages [i ].ContainsString != "" {
151+ containsNeeded ++
152+ if strings .Contains (message_content , messages .Messages [i ].ContainsString ) {
153+ containsFound ++
154+ }
155+ }
156+
157+ if containsNeeded == containsFound {
158+ msg .Ack (true )
159+ fmt .Printf ("Acked msg-%04d\n " , messagesReceived )
160+ ackMessage = true
161+ messages .Messages = append (messages .Messages [:i ], messages .Messages [i + 1 :]... )
162+ }
163+ }
96164 }
97165 }
98- }
99166
100- return nil
101- }
167+ err = SaveMessageToFile (msg , outputDir , messagesReceived , isContentJSON , ackMessage )
168+ if err != nil {
169+ return fmt .Errorf ("Save message: %s" , err )
170+ }
102171
103- func SaveMessageToFile (body []byte , outputDir string , counter uint ) error {
104- filePath := GenerateFilePath (outputDir , counter )
105- err := ioutil .WriteFile (filePath , body , 0644 )
106- if err != nil {
107- return err
108172 }
109173
110- fmt .Println (filePath )
111-
112174 return nil
113175}
114176
@@ -125,6 +187,8 @@ func GetProperties(msg amqp.Delivery) map[string]interface{} {
125187 "reply_to" : msg .ReplyTo ,
126188 "type" : msg .Type ,
127189 "user_id" : msg .UserId ,
190+ "exchange" : msg .Exchange ,
191+ "routing_key" : msg .RoutingKey ,
128192 }
129193
130194 if ! msg .Timestamp .IsZero () {
@@ -140,17 +204,25 @@ func GetProperties(msg amqp.Delivery) map[string]interface{} {
140204 return props
141205}
142206
143- func SavePropsAndHeadersToFile (msg amqp.Delivery , outputDir string , counter uint ) error {
207+ func SaveMessageToFile (msg amqp.Delivery , outputDir string , counter uint , isContentJSON bool , wasAcked bool ) error {
144208 extras := make (map [string ]interface {})
145209 extras ["properties" ] = GetProperties (msg )
146210 extras ["headers" ] = msg .Headers
211+ extras ["acked" ] = wasAcked
212+ if isContentJSON {
213+ var content interface {}
214+ json .Unmarshal (msg .Body , & content )
215+ extras ["content" ] = content
216+ } else {
217+ extras ["content" ] = string (msg .Body [:])
218+ }
147219
148220 data , err := json .MarshalIndent (extras , "" , " " )
149221 if err != nil {
150222 return err
151223 }
152224
153- filePath := GenerateFilePath (outputDir , counter ) + "-headers+properties.json"
225+ filePath := GenerateFilePath (outputDir , counter )
154226 err = ioutil .WriteFile (filePath , data , 0644 )
155227 if err != nil {
156228 return err
@@ -162,11 +234,20 @@ func SavePropsAndHeadersToFile(msg amqp.Delivery, outputDir string, counter uint
162234}
163235
164236func GenerateFilePath (outputDir string , counter uint ) string {
165- return path .Join (outputDir , fmt .Sprintf ("msg-%04d" , counter ))
237+ return path .Join (outputDir , fmt .Sprintf ("msg-%04d.json " , counter ))
166238}
167239
168240func VerboseLog (msg string ) {
169241 if * verbose {
170242 fmt .Println ("*" , msg )
171243 }
172244}
245+
246+ func NumDecPlaces (v float64 ) int {
247+ s := strconv .FormatFloat (v , 'f' , - 1 , 64 )
248+ i := strings .IndexByte (s , '.' )
249+ if i > - 1 {
250+ return len (s ) - i - 1
251+ }
252+ return 0
253+ }
0 commit comments